Skip to content

Commit

Permalink
Merge pull request #126 from phlip9/fix_ffi_bugs
Browse files Browse the repository at this point in the history
ffi: fix crashes from use-after-free. fix memory leaks. correctness cleanups.
  • Loading branch information
khoren93 authored Feb 10, 2024
2 parents f987a91 + c1ffa3b commit 5137b7c
Show file tree
Hide file tree
Showing 10 changed files with 260 additions and 185 deletions.
3 changes: 0 additions & 3 deletions ffigen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ headers:
- "src/native_zxing.h"
include-directives:
- "src/native_zxing.h"
functions:
exclude:
- "resultToCodeResult"
preamble: |
// ignore_for_file: always_specify_types
// ignore_for_file: camel_case_types
Expand Down
97 changes: 48 additions & 49 deletions lib/generated_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,22 @@ class GeneratedBindings {
: _lookup = lookup;

/// @brief Enables or disables the logging of the library.
/// @param enable Whether to enable or disable the logging.
///
/// @param enabled
/// @param enabled Whether to enable or disable the logging.
void setLogEnabled(
int enable,
bool enabled,
) {
return _setLogEnabled(
enable,
enabled,
);
}

late final _setLogEnabledPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>('setLogEnabled');
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Bool)>>('setLogEnabled');
late final _setLogEnabled =
_setLogEnabledPtr.asFunction<void Function(int)>();
_setLogEnabledPtr.asFunction<void Function(bool)>();

/// Returns the version of the zxing-cpp library.
/// Returns the version of the zxing-cpp library. Pointer has a static lifetime and must not be freed.
///
/// @return The version of the zxing-cpp library.
ffi.Pointer<ffi.Char> version() {
Expand All @@ -57,7 +56,7 @@ class GeneratedBindings {
_versionPtr.asFunction<ffi.Pointer<ffi.Char> Function()>();

/// @brief Read barcode from image bytes.
/// @param bytes Image bytes.
/// @param bytes Image bytes. Owned pointer. Will be freed by native code.
/// @param imageFormat Image format.
/// @param format Specify a set of BarcodeFormats that should be searched for.
/// @param width Image width in pixels.
Expand All @@ -68,16 +67,16 @@ class GeneratedBindings {
/// @param tryRotate Also try detecting code in 90, 180 and 270 degree rotated images.
/// @return The barcode result.
CodeResult readBarcode(
ffi.Pointer<ffi.Char> bytes,
ffi.Pointer<ffi.Uint8> bytes,
int imageFormat,
int format,
int width,
int height,
int cropWidth,
int cropHeight,
int tryHarder,
int tryRotate,
int tryInvert,
bool tryHarder,
bool tryRotate,
bool tryInvert,
) {
return _readBarcode(
bytes,
Expand All @@ -96,22 +95,22 @@ class GeneratedBindings {
late final _readBarcodePtr = _lookup<
ffi.NativeFunction<
CodeResult Function(
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Uint8>,
ffi.Int,
ffi.Int,
ffi.Int,
ffi.Int,
ffi.Int,
ffi.Int,
ffi.Int,
ffi.Int,
ffi.Int)>>('readBarcode');
ffi.Bool,
ffi.Bool,
ffi.Bool)>>('readBarcode');
late final _readBarcode = _readBarcodePtr.asFunction<
CodeResult Function(ffi.Pointer<ffi.Char>, int, int, int, int, int, int,
int, int, int)>();
CodeResult Function(ffi.Pointer<ffi.Uint8>, int, int, int, int, int, int,
bool, bool, bool)>();

/// @brief Read barcodes from image bytes.
/// @param bytes Image bytes.
/// @param bytes Image bytes. Owned pointer. Will be freed by native code.
/// @param imageFormat Image format.
/// @param format Specify a set of BarcodeFormats that should be searched for.
/// @param width Image width in pixels.
Expand All @@ -122,16 +121,16 @@ class GeneratedBindings {
/// @param tryRotate Also try detecting code in 90, 180 and 270 degree rotated images.
/// @return The barcode results.
CodeResults readBarcodes(
ffi.Pointer<ffi.Char> bytes,
ffi.Pointer<ffi.Uint8> bytes,
int imageFormat,
int format,
int width,
int height,
int cropWidth,
int cropHeight,
int tryHarder,
int tryRotate,
int tryInvert,
bool tryHarder,
bool tryRotate,
bool tryInvert,
) {
return _readBarcodes(
bytes,
Expand All @@ -150,28 +149,28 @@ class GeneratedBindings {
late final _readBarcodesPtr = _lookup<
ffi.NativeFunction<
CodeResults Function(
ffi.Pointer<ffi.Char>,
ffi.Int,
ffi.Int,
ffi.Pointer<ffi.Uint8>,
ffi.Int,
ffi.Int,
ffi.Int,
ffi.Int,
ffi.Int,
ffi.Int,
ffi.Int)>>('readBarcodes');
ffi.Bool,
ffi.Bool,
ffi.Bool)>>('readBarcodes');
late final _readBarcodes = _readBarcodesPtr.asFunction<
CodeResults Function(ffi.Pointer<ffi.Char>, int, int, int, int, int, int,
int, int, int)>();
CodeResults Function(ffi.Pointer<ffi.Uint8>, int, int, int, int, int, int,
bool, bool, bool)>();

/// @brief Encode a string into a barcode
/// @param contents The string to encode
/// @param contents The string to encode. Owned pointer. Will be freed by native code.
/// @param width The width of the barcode in pixels.
/// @param height The height of the barcode in pixels.
/// @param format The format of the barcode
/// @param margin The margin of the barcode
/// @param eccLevel The error correction level of the barcode. Used for Aztec, PDF417, and QRCode only, [0-8].
/// @return The barcode data
/// @return The barcode data.
EncodeResult encodeBarcode(
ffi.Pointer<ffi.Char> contents,
int width,
Expand Down Expand Up @@ -243,18 +242,18 @@ final class Pos extends ffi.Struct {

/// @brief The CodeResult class encapsulates the result of decoding a barcode within an image.
final class CodeResult extends ffi.Struct {
/// < The decoded text
/// < The decoded text. Owned pointer. Must be freed by Dart code if not null.
external ffi.Pointer<ffi.Char> text;

/// < Whether the barcode was successfully decoded
@ffi.Int()
external int isValid;
@ffi.Bool()
external bool isValid;

/// < The error message
/// < The error message. Owned pointer. Must be freed by Dart code if not null.
external ffi.Pointer<ffi.Char> error;

/// < The bytes is the raw / standard content without any modifications like character set conversions
external ffi.Pointer<ffi.UnsignedChar> bytes;
/// < The bytes is the raw content without any character set conversions. Owned pointer. Must be freed by Dart code if not null.
external ffi.Pointer<ffi.Uint8> bytes;

/// < The length of the bytes
@ffi.Int()
Expand All @@ -265,15 +264,15 @@ final class CodeResult extends ffi.Struct {
external int format;

/// < The position of the barcode within the image
external ffi.Pointer<Pos> pos;
external Pos pos;

/// < Whether the barcode was inverted
@ffi.Int()
external int isInverted;
@ffi.Bool()
external bool isInverted;

/// < Whether the barcode was mirrored
@ffi.Int()
external int isMirrored;
@ffi.Bool()
external bool isMirrored;

/// < The duration of the decoding in milliseconds
@ffi.Int()
Expand All @@ -286,7 +285,7 @@ final class CodeResults extends ffi.Struct {
@ffi.Int()
external int count;

/// < The results of the barcode decoding
/// < The results of the barcode decoding. Owned pointer. Must be freed by Dart code.
external ffi.Pointer<CodeResult> results;

/// < The duration of the decoding in milliseconds
Expand All @@ -297,23 +296,23 @@ final class CodeResults extends ffi.Struct {
/// @brief EncodeResult encapsulates the result of encoding a barcode.
final class EncodeResult extends ffi.Struct {
/// < Whether the barcode was successfully encoded
@ffi.Int()
external int isValid;
@ffi.Bool()
external bool isValid;

/// < The encoded text
/// < The encoded text. Owned pointer. Must be freed by Dart code if not null.
external ffi.Pointer<ffi.Char> text;

/// < The format of the barcode
@ffi.Int()
external int format;

/// < The encoded data
external ffi.Pointer<ffi.SignedChar> data;
/// < The encoded data. Owned pointer. Must be freed by Dart code if not null.
external ffi.Pointer<ffi.Uint8> data;

/// < The length of the encoded data
@ffi.Int()
external int length;

/// < The error message
/// < The error message. Owned pointer. Must be freed by Dart code if not null.
external ffi.Pointer<ffi.Char> error;
}
8 changes: 4 additions & 4 deletions lib/src/logic/barcode_reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,16 @@ Code _readBarcode(
) {
return bindings
.readBarcode(
bytes.allocatePointer(),
bytes.copyToNativePointer(),
params?.imageFormat ?? zx.ImageFormat.lum,
params?.format ?? Format.any,
width,
height,
params?.cropWidth ?? 0,
params?.cropHeight ?? 0,
params?.tryHarder ?? false ? 1 : 0,
params?.tryRotate ?? true ? 1 : 0,
params?.tryInverted ?? false ? 1 : 0,
params?.tryHarder ?? false,
params?.tryRotate ?? true,
params?.tryInverted ?? false,
)
.toCode();
}
21 changes: 14 additions & 7 deletions lib/src/logic/barcodes_reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,27 @@ Codes _readBarcodes(
DecodeParams? params,
) {
final CodeResults result = bindings.readBarcodes(
bytes.allocatePointer(),
bytes.copyToNativePointer(),
params?.imageFormat ?? zx.ImageFormat.lum,
params?.format ?? Format.any,
width,
height,
params?.cropWidth ?? 0,
params?.cropHeight ?? 0,
params?.tryHarder ?? false ? 1 : 0,
params?.tryRotate ?? true ? 1 : 0,
params?.tryInverted ?? false ? 1 : 0,
params?.tryHarder ?? false,
params?.tryRotate ?? true,
params?.tryInverted ?? false,
);
final List<Code> results = <Code>[];

final List<Code> codes = <Code>[];

if (result.count == 0 || result.results == nullptr) {
return Codes(codes: codes, duration: result.duration);
}

for (int i = 0; i < result.count; i++) {
results.add(result.results.elementAt(i).ref.toCode());
codes.add(result.results.elementAt(i).ref.toCode());
}
return Codes(codes: results, duration: result.duration);
malloc.free(result.results);
return Codes(codes: codes, duration: result.duration);
}
17 changes: 8 additions & 9 deletions lib/src/logic/zxing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,17 @@ part 'camera_stream.dart';
String zxingVersion() => bindings.version().cast<Utf8>().toDartString();

/// Enables or disables the logging of the library
void setZxingLogEnabled(bool enabled) =>
bindings.setLogEnabled(enabled ? 1 : 0);
void setZxingLogEnabled(bool enabled) => bindings.setLogEnabled(enabled);

/// Returns a readable barcode format name
String zxingBarcodeFormatName(int format) => barcodeNames[format] ?? 'Unknown';

extension Uint8ListBlobConversion on Uint8List {
/// Allocates a pointer filled with the Uint8List data.
Pointer<Char> allocatePointer() {
final Pointer<Int8> blob = calloc<Int8>(length);
final Int8List blobBytes = blob.asTypedList(length);
blobBytes.setAll(0, this);
return blob.cast<Char>();
extension Uint8ListExt on Uint8List {
/// Copy the [Uint8List] into a freshly allocated [Pointer<Uint8>].
Pointer<Uint8> copyToNativePointer() {
final Pointer<Uint8> ptr = malloc<Uint8>(length);
final Uint8List view = ptr.asTypedList(length);
view.setAll(0, this);
return ptr;
}
}
Loading

0 comments on commit 5137b7c

Please sign in to comment.