From bb922a807900f3364751dd09745eb5796cf782f2 Mon Sep 17 00:00:00 2001 From: Philip Hayes Date: Mon, 29 Jan 2024 20:02:01 +0800 Subject: [PATCH 1/7] ffi: fix `encodeBarcode` crash from use-after-free bug Certain platforms (iOS) were crashing (!) when creating QR code images with specific image dimensions. This simplified patch fixes the bug causing these crashes, though it leaks the memory. Bug: `encodeBarcode` was returning `matrix.data()`, a raw borrowed pointer from a `std::vector`, across the FFI boundary. When the function went out of scope, the `std::vector` would be free'd and the generated dart ffi code would be left holding a dangling pointer. To fix this, we just need to return an owned pointer, which means copying the data out of the `std::vector` first before returning. --- src/native_zxing.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/native_zxing.cpp b/src/native_zxing.cpp index 69cdcff..21c3a61 100644 --- a/src/native_zxing.cpp +++ b/src/native_zxing.cpp @@ -5,8 +5,9 @@ #include "native_zxing.h" // #include "ZXVersion.h" // This file is not existing for iOS -#include +#include #include +#include #include using namespace ZXing; @@ -125,8 +126,16 @@ extern "C" { auto writer = MultiFormatWriter(BarcodeFormat(format)).setMargin(margin).setEccLevel(eccLevel).setEncoding(CharacterSet::UTF8); auto bitMatrix = writer.encode(contents, width, height); - result.data = ToMatrix(bitMatrix).data(); - result.length = bitMatrix.width() * bitMatrix.height(); + auto matrix = ToMatrix(bitMatrix); + + // We need to return an owned pointer across the ffi boundary. Copy + // the output (again). + auto length = matrix.size(); + auto data = new int8_t[length]; + std::copy(matrix.begin(), matrix.end(), data); + + result.length = length; + result.data = data; result.isValid = true; } catch (const exception &e) From c41b5942beaacaa9e4828d778227b8c4152d9396 Mon Sep 17 00:00:00 2001 From: Philip Hayes Date: Tue, 30 Jan 2024 15:57:03 +0800 Subject: [PATCH 2/7] ffi: fix memory leaks. owned pointers need to be freed. Owned pointers returned from the native code must be freed to prevent memory leaks. --- lib/src/utils/extentions.dart | 63 +++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/lib/src/utils/extentions.dart b/lib/src/utils/extentions.dart index 8b08d0f..f07445a 100644 --- a/lib/src/utils/extentions.dart +++ b/lib/src/utils/extentions.dart @@ -5,16 +5,59 @@ import 'package:ffi/ffi.dart'; import '../../zxing_mobile.dart'; +/// From an owned pointer allocated in native code, copy the data into the Dart +/// VM Heap as a [Uint32List] and then immediately `free` the owned ffi pointer. +Uint32List? copyUint32ListFromOwnedFfiPtr( + Pointer data, + int length, +) { + if (data == nullptr || length == 0) { + return null; + } + + final Uint32List out = + Uint32List.fromList(data.cast().asTypedList(length)); + malloc.free(data); + return out; +} + +/// From an owned pointer allocated in native code, copy the data into the Dart +/// VM Heap as a [Uint8List] and then immediately `free` the owned ffi pointer. +Uint8List? copyUint8ListFromOwnedFfiPtr( + Pointer data, int length) { + if (data == nullptr || length == 0) { + return null; + } + + final Uint8List out = + Uint8List.fromList(data.cast().asTypedList(length)); + malloc.free(data); + return out; +} + +/// From an owned, UTF-8 encoded C-string (null-byte terminated) allocated in +/// native code, copy the string into the Dart VM Heap as a [String]a and then +/// immediately `free` the owned pointer. +String? copyStringFromOwnedFfiPtr(Pointer text) { + if (text == nullptr) { + return null; + } + + final String out = text.cast().toDartString(); + malloc.free(text); + return out; +} + extension CodeExt on CodeResult { Code toCode() { return Code( - text: text == nullptr ? null : text.cast().toDartString(), + text: copyStringFromOwnedFfiPtr(text), isValid: isValid == 1, - error: error == nullptr ? null : error.cast().toDartString(), - rawBytes: bytes == nullptr - ? null - : Uint8List.fromList(bytes.cast().asTypedList(length)), + error: copyStringFromOwnedFfiPtr(error), + rawBytes: copyUint8ListFromOwnedFfiPtr(bytes, length), format: format, + // TODO(phlip9): this should be passed by value... or free'd... Otherwise + // this currently leaks memory. position: pos == nullptr ? null : pos.ref.toPosition(), isInverted: isInverted == 1, isMirrored: isMirrored == 1, @@ -27,16 +70,14 @@ extension EncodeExt on EncodeResult { Encode toEncode() => Encode( isValid == 1, format, - text == nullptr ? null : text.cast().toDartString(), - data == nullptr - ? null - : Uint32List.fromList(data.cast().asTypedList(length)), + copyStringFromOwnedFfiPtr(text), + copyUint32ListFromOwnedFfiPtr(data, length), length, - error == nullptr ? null : error.cast().toDartString(), + copyStringFromOwnedFfiPtr(error), ); } -extension PoeExt on Pos { +extension PosExt on Pos { Position toPosition() => Position( imageWidth, imageHeight, From a29369eb96ac41dfc46c99ca72eb8fab64f99fba Mon Sep 17 00:00:00 2001 From: Philip Hayes Date: Tue, 30 Jan 2024 16:27:10 +0800 Subject: [PATCH 3/7] ffi: just pass `Pos` by value. avoid having to free. Before this change, we leaked the memory for each `Pos` returned from the native code. To keep things simple, we'll just return `Pos` by value, since it's just a bag of `int`s. Let's also be a bit more careful and ensure `CodeResult`s are default initialized to avoid returning any uninit data. --- lib/generated_bindings.dart | 2 +- lib/src/utils/extentions.dart | 4 +--- src/native_zxing.cpp | 14 +++++++------- src/native_zxing.h | 4 ++-- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index 2a3f331..bae6646 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -265,7 +265,7 @@ final class CodeResult extends ffi.Struct { external int format; /// < The position of the barcode within the image - external ffi.Pointer pos; + external Pos pos; /// < Whether the barcode was inverted @ffi.Int() diff --git a/lib/src/utils/extentions.dart b/lib/src/utils/extentions.dart index f07445a..e2b2cda 100644 --- a/lib/src/utils/extentions.dart +++ b/lib/src/utils/extentions.dart @@ -56,9 +56,7 @@ extension CodeExt on CodeResult { error: copyStringFromOwnedFfiPtr(error), rawBytes: copyUint8ListFromOwnedFfiPtr(bytes, length), format: format, - // TODO(phlip9): this should be passed by value... or free'd... Otherwise - // this currently leaks memory. - position: pos == nullptr ? null : pos.ref.toPosition(), + position: pos.toPosition(), isInverted: isInverted == 1, isMirrored: isMirrored == 1, duration: duration, diff --git a/src/native_zxing.cpp b/src/native_zxing.cpp index 21c3a61..70a9616 100644 --- a/src/native_zxing.cpp +++ b/src/native_zxing.cpp @@ -38,7 +38,7 @@ extern "C" auto tr = p.topRight(); auto bl = p.bottomLeft(); auto br = p.bottomRight(); - code->pos = new Pos{0, 0, tl.x, tl.y, tr.x, tr.y, bl.x, bl.y, br.x, br.y}; + code->pos = Pos{0, 0, tl.x, tl.y, tr.x, tr.y, bl.x, bl.y, br.x, br.y}; code->isInverted = result.isInverted(); code->isMirrored = result.isMirrored(); @@ -72,13 +72,13 @@ extern "C" delete[] bytes; - struct CodeResult code; + struct CodeResult code {}; resultToCodeResult(&code, result); int evalInMillis = static_cast(get_now() - start); code.duration = evalInMillis; - code.pos->imageWidth = width; - code.pos->imageHeight = height; + code.pos.imageWidth = width; + code.pos.imageHeight = height; platform_log("Read Barcode in: %d ms\n", code.duration); return code; } @@ -104,11 +104,11 @@ extern "C" int i = 0; for (auto &result : results) { - struct CodeResult code; + struct CodeResult code {}; resultToCodeResult(&code, result); code.duration = evalInMillis; - code.pos->imageWidth = width; - code.pos->imageHeight = height; + code.pos.imageWidth = width; + code.pos.imageHeight = height; codes[i] = code; i++; } diff --git a/src/native_zxing.h b/src/native_zxing.h index 6912d9e..ccde79d 100644 --- a/src/native_zxing.h +++ b/src/native_zxing.h @@ -32,7 +32,7 @@ extern "C" const unsigned char *bytes; ///< The bytes is the raw / standard content without any modifications like character set conversions int length; ///< The length of the bytes int format; ///< The format of the barcode - struct Pos *pos; ///< The position of the barcode within the image + struct Pos pos; ///< The position of the barcode within the image int isInverted; ///< Whether the barcode was inverted int isMirrored; ///< Whether the barcode was mirrored int duration; ///< The duration of the decoding in milliseconds @@ -121,4 +121,4 @@ extern "C" #ifdef __cplusplus } -#endif \ No newline at end of file +#endif From 50793ef180b4c8952e3cee4e1c7ee807af18393f Mon Sep 17 00:00:00 2001 From: Philip Hayes Date: Tue, 30 Jan 2024 19:08:06 +0800 Subject: [PATCH 4/7] ffi: avoid abi-dependent types. fix more memory leaks and use-after-free - "bytes" should be `uint8_t*` - C-strings should be `char*` - Owned pointers should not be `const`. - In `native_zxing.h`, clarify the expected ownership of each pointer type passed to and returned from the native code. "Owned" pointers must to be freed by their callees while "borrowed" pointers must be freed by their callers. - Fix use-after-free bug in `resultToCodeResult` which was returning a reference to a dropped `ByteArray` via `code->bytes = result.bytes().data()`. --- lib/generated_bindings.dart | 43 +++++++++++++++--------------- lib/src/logic/barcode_reader.dart | 2 +- lib/src/logic/barcodes_reader.dart | 3 ++- lib/src/logic/zxing.dart | 14 +++++----- lib/src/utils/extentions.dart | 8 +++--- src/native_zxing.cpp | 26 +++++++++--------- src/native_zxing.h | 39 +++++++++++++++------------ 7 files changed, 70 insertions(+), 65 deletions(-) diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index bae6646..e3732a1 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -28,9 +28,8 @@ 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 enable Whether to enable or disable the logging. void setLogEnabled( int enable, ) { @@ -44,7 +43,7 @@ class GeneratedBindings { late final _setLogEnabled = _setLogEnabledPtr.asFunction(); - /// 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 version() { @@ -57,7 +56,7 @@ class GeneratedBindings { _versionPtr.asFunction 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. @@ -68,7 +67,7 @@ class GeneratedBindings { /// @param tryRotate Also try detecting code in 90, 180 and 270 degree rotated images. /// @return The barcode result. CodeResult readBarcode( - ffi.Pointer bytes, + ffi.Pointer bytes, int imageFormat, int format, int width, @@ -96,7 +95,7 @@ class GeneratedBindings { late final _readBarcodePtr = _lookup< ffi.NativeFunction< CodeResult Function( - ffi.Pointer, + ffi.Pointer, ffi.Int, ffi.Int, ffi.Int, @@ -107,11 +106,11 @@ class GeneratedBindings { ffi.Int, ffi.Int)>>('readBarcode'); late final _readBarcode = _readBarcodePtr.asFunction< - CodeResult Function(ffi.Pointer, int, int, int, int, int, int, + CodeResult Function(ffi.Pointer, int, int, int, int, int, int, int, int, int)>(); /// @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. @@ -122,7 +121,7 @@ class GeneratedBindings { /// @param tryRotate Also try detecting code in 90, 180 and 270 degree rotated images. /// @return The barcode results. CodeResults readBarcodes( - ffi.Pointer bytes, + ffi.Pointer bytes, int imageFormat, int format, int width, @@ -150,7 +149,7 @@ class GeneratedBindings { late final _readBarcodesPtr = _lookup< ffi.NativeFunction< CodeResults Function( - ffi.Pointer, + ffi.Pointer, ffi.Int, ffi.Int, ffi.Int, @@ -161,17 +160,17 @@ class GeneratedBindings { ffi.Int, ffi.Int)>>('readBarcodes'); late final _readBarcodes = _readBarcodesPtr.asFunction< - CodeResults Function(ffi.Pointer, int, int, int, int, int, int, + CodeResults Function(ffi.Pointer, int, int, int, int, int, int, int, int, int)>(); /// @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 contents, int width, @@ -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 text; /// < Whether the barcode was successfully decoded @ffi.Int() external int isValid; - /// < The error message + /// < The error message. Owned pointer. Must be freed by Dart code if not null. external ffi.Pointer error; - /// < The bytes is the raw / standard content without any modifications like character set conversions - external ffi.Pointer 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 bytes; /// < The length of the bytes @ffi.Int() @@ -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 results; /// < The duration of the decoding in milliseconds @@ -300,20 +299,20 @@ final class EncodeResult extends ffi.Struct { @ffi.Int() external int isValid; - /// < The encoded text + /// < The encoded text. Owned pointer. Must be freed by Dart code if not null. external ffi.Pointer text; /// < The format of the barcode @ffi.Int() external int format; - /// < The encoded data - external ffi.Pointer data; + /// < The encoded data. Owned pointer. Must be freed by Dart code if not null. + external ffi.Pointer 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 error; } diff --git a/lib/src/logic/barcode_reader.dart b/lib/src/logic/barcode_reader.dart index 1e3d15e..c7dadb6 100644 --- a/lib/src/logic/barcode_reader.dart +++ b/lib/src/logic/barcode_reader.dart @@ -67,7 +67,7 @@ Code _readBarcode( ) { return bindings .readBarcode( - bytes.allocatePointer(), + bytes.copyToNativePointer(), params?.imageFormat ?? zx.ImageFormat.lum, params?.format ?? Format.any, width, diff --git a/lib/src/logic/barcodes_reader.dart b/lib/src/logic/barcodes_reader.dart index 4cbde5a..e2ab00a 100644 --- a/lib/src/logic/barcodes_reader.dart +++ b/lib/src/logic/barcodes_reader.dart @@ -66,7 +66,7 @@ Codes _readBarcodes( DecodeParams? params, ) { final CodeResults result = bindings.readBarcodes( - bytes.allocatePointer(), + bytes.copyToNativePointer(), params?.imageFormat ?? zx.ImageFormat.lum, params?.format ?? Format.any, width, @@ -81,5 +81,6 @@ Codes _readBarcodes( for (int i = 0; i < result.count; i++) { results.add(result.results.elementAt(i).ref.toCode()); } + malloc.free(result.results); return Codes(codes: results, duration: result.duration); } diff --git a/lib/src/logic/zxing.dart b/lib/src/logic/zxing.dart index 1a01d16..2b695f0 100644 --- a/lib/src/logic/zxing.dart +++ b/lib/src/logic/zxing.dart @@ -33,12 +33,12 @@ void setZxingLogEnabled(bool 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 allocatePointer() { - final Pointer blob = calloc(length); - final Int8List blobBytes = blob.asTypedList(length); - blobBytes.setAll(0, this); - return blob.cast(); +extension Uint8ListExt on Uint8List { + /// Copy the [Uint8List] into a freshly allocated [Pointer]. + Pointer copyToNativePointer() { + final Pointer ptr = malloc(length); + final Uint8List view = ptr.asTypedList(length); + view.setAll(0, this); + return ptr; } } diff --git a/lib/src/utils/extentions.dart b/lib/src/utils/extentions.dart index e2b2cda..c0a5fa3 100644 --- a/lib/src/utils/extentions.dart +++ b/lib/src/utils/extentions.dart @@ -8,7 +8,7 @@ import '../../zxing_mobile.dart'; /// From an owned pointer allocated in native code, copy the data into the Dart /// VM Heap as a [Uint32List] and then immediately `free` the owned ffi pointer. Uint32List? copyUint32ListFromOwnedFfiPtr( - Pointer data, + Pointer data, int length, ) { if (data == nullptr || length == 0) { @@ -23,14 +23,12 @@ Uint32List? copyUint32ListFromOwnedFfiPtr( /// From an owned pointer allocated in native code, copy the data into the Dart /// VM Heap as a [Uint8List] and then immediately `free` the owned ffi pointer. -Uint8List? copyUint8ListFromOwnedFfiPtr( - Pointer data, int length) { +Uint8List? copyUint8ListFromOwnedFfiPtr(Pointer data, int length) { if (data == nullptr || length == 0) { return null; } - final Uint8List out = - Uint8List.fromList(data.cast().asTypedList(length)); + final Uint8List out = Uint8List.fromList(data.asTypedList(length)); malloc.free(data); return out; } diff --git a/src/native_zxing.cpp b/src/native_zxing.cpp index 70a9616..b4c4ab9 100644 --- a/src/native_zxing.cpp +++ b/src/native_zxing.cpp @@ -7,8 +7,9 @@ #include #include +#include +#include #include -#include using namespace ZXing; using namespace std; @@ -29,9 +30,11 @@ extern "C" code->format = static_cast(result.format()); - // TODO: this needs to be allocated and coped as well (see text above). Will also require a delete in some flutter code, I assume - code->bytes = result.bytes().data(); - code->length = result.bytes().size(); + auto length = result.bytes().size(); + auto* bytes = new uint8_t[length]; + std::copy(result.bytes().begin(), result.bytes().end(), bytes); + code->bytes = bytes; + code->length = length; auto p = result.position(); auto tl = p.topLeft(); @@ -58,18 +61,17 @@ extern "C" } FUNCTION_ATTRIBUTE - struct CodeResult readBarcode(char *bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, int tryHarder, int tryRotate, int tryInvert) + struct CodeResult readBarcode(uint8_t* bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, int tryHarder, int tryRotate, int tryInvert) { long long start = get_now(); - ImageView image{reinterpret_cast(bytes), width, height, ImageFormat(imageFormat)}; + ImageView image {bytes, width, height, ImageFormat(imageFormat)}; if (cropWidth > 0 && cropHeight > 0 && cropWidth < width && cropHeight < height) { image = image.cropped(width / 2 - cropWidth / 2, height / 2 - cropHeight / 2, cropWidth, cropHeight); } ReaderOptions hints = ReaderOptions().setTryHarder(tryHarder).setTryRotate(tryRotate).setFormats(BarcodeFormat(format)).setTryInvert(tryInvert).setReturnErrors(true); Result result = ReadBarcode(image, hints); - delete[] bytes; struct CodeResult code {}; @@ -84,11 +86,11 @@ extern "C" } FUNCTION_ATTRIBUTE - struct CodeResults readBarcodes(char *bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, int tryHarder, int tryRotate, int tryInvert) + struct CodeResults readBarcodes(uint8_t* bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, int tryHarder, int tryRotate, int tryInvert) { long long start = get_now(); - ImageView image{reinterpret_cast(bytes), width, height, ImageFormat(imageFormat)}; + ImageView image{bytes, width, height, ImageFormat(imageFormat)}; if (cropWidth > 0 && cropHeight > 0 && cropWidth < width && cropHeight < height) { image = image.cropped(width / 2 - cropWidth / 2, height / 2 - cropHeight / 2, cropWidth, cropHeight); @@ -117,7 +119,7 @@ extern "C" } FUNCTION_ATTRIBUTE - struct EncodeResult encodeBarcode(char *contents, int width, int height, int format, int margin, int eccLevel) + struct EncodeResult encodeBarcode(char* contents, int width, int height, int format, int margin, int eccLevel) { long long start = get_now(); @@ -126,12 +128,12 @@ extern "C" { auto writer = MultiFormatWriter(BarcodeFormat(format)).setMargin(margin).setEccLevel(eccLevel).setEncoding(CharacterSet::UTF8); auto bitMatrix = writer.encode(contents, width, height); - auto matrix = ToMatrix(bitMatrix); + auto matrix = ToMatrix(bitMatrix); // We need to return an owned pointer across the ffi boundary. Copy // the output (again). auto length = matrix.size(); - auto data = new int8_t[length]; + auto* data = new uint8_t[length]; std::copy(matrix.begin(), matrix.end(), data); result.length = length; diff --git a/src/native_zxing.h b/src/native_zxing.h index ccde79d..9d5d1e3 100644 --- a/src/native_zxing.h +++ b/src/native_zxing.h @@ -1,3 +1,9 @@ +#ifdef __cplusplus + #include +#else + #include +#endif + #ifdef __cplusplus extern "C" { @@ -26,10 +32,10 @@ extern "C" */ struct CodeResult { - char *text; ///< The decoded text + char *text; ///< The decoded text. Owned pointer. Must be freed by Dart code if not null. int isValid; ///< Whether the barcode was successfully decoded - char *error; ///< The error message - const unsigned char *bytes; ///< The bytes is the raw / standard content without any modifications like character set conversions + char *error; ///< The error message. Owned pointer. Must be freed by Dart code if not null. + uint8_t* bytes; ///< The bytes is the raw content without any character set conversions. Owned pointer. Must be freed by Dart code if not null. int length; ///< The length of the bytes int format; ///< The format of the barcode struct Pos pos; ///< The position of the barcode within the image @@ -44,7 +50,7 @@ extern "C" struct CodeResults { int count; ///< The number of barcodes detected - struct CodeResult *results; ///< The results of the barcode decoding + struct CodeResult *results; ///< The results of the barcode decoding. Owned pointer. Must be freed by Dart code. int duration; ///< The duration of the decoding in milliseconds }; @@ -55,23 +61,22 @@ extern "C" struct EncodeResult { int isValid; ///< Whether the barcode was successfully encoded - char *text; ///< The encoded text + char *text; ///< The encoded text. Owned pointer. Must be freed by Dart code if not null. int format; ///< The format of the barcode - const signed char *data; ///< The encoded data + uint8_t* data; ///< The encoded data. Owned pointer. Must be freed by Dart code if not null. int length; ///< The length of the encoded data - char *error; ///< The error message + char *error; ///< The error message. Owned pointer. Must be freed by Dart code if not null. }; /** * @brief Enables or disables the logging of the library. - * @param enable Whether to enable or disable the logging. * - * @param enabled + * @param enable Whether to enable or disable the logging. */ void setLogEnabled(int enable); /** - * 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. */ @@ -79,7 +84,7 @@ extern "C" /** * @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. @@ -90,11 +95,11 @@ extern "C" * @param tryRotate Also try detecting code in 90, 180 and 270 degree rotated images. * @return The barcode result. */ - struct CodeResult readBarcode(char *bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, int tryHarder, int tryRotate, int tryInvert); + struct CodeResult readBarcode(uint8_t* bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, int tryHarder, int tryRotate, int tryInvert); /** * @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. @@ -105,19 +110,19 @@ extern "C" * @param tryRotate Also try detecting code in 90, 180 and 270 degree rotated images. * @return The barcode results. */ - struct CodeResults readBarcodes(char *bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, int tryHarder, int tryRotate, int tryInvert); + struct CodeResults readBarcodes(uint8_t* bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, int tryHarder, int tryRotate, int tryInvert); /** * @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. */ - struct EncodeResult encodeBarcode(char *contents, int width, int height, int format, int margin, int eccLevel); + struct EncodeResult encodeBarcode(char* contents, int width, int height, int format, int margin, int eccLevel); #ifdef __cplusplus } From cb7c534c5a7505ff145dfb19df7b6cc7cc0ea033 Mon Sep 17 00:00:00 2001 From: Philip Hayes Date: Wed, 31 Jan 2024 12:15:03 +0800 Subject: [PATCH 5/7] ffi: need free after `readBarcodes`. avoid zero-sized allocation. - For `readBarcodes`, ensure that we `free` the allocated block of `CodeResult`s on the Dart side after we're done with it. - Add two extra checks in Dart and Native `readBarcodes` resp. to ensure we don't make a zero-sized allocation when there are no barcode results. Zero-sized allocations are platform dependent/semi-undefined. --- ffigen.yaml | 3 - lib/src/logic/barcodes_reader.dart | 12 ++- src/native_zxing.cpp | 117 +++++++++++++++++------------ 3 files changed, 77 insertions(+), 55 deletions(-) diff --git a/ffigen.yaml b/ffigen.yaml index 0bc4f4f..e4f792a 100644 --- a/ffigen.yaml +++ b/ffigen.yaml @@ -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 diff --git a/lib/src/logic/barcodes_reader.dart b/lib/src/logic/barcodes_reader.dart index e2ab00a..735241c 100644 --- a/lib/src/logic/barcodes_reader.dart +++ b/lib/src/logic/barcodes_reader.dart @@ -77,10 +77,16 @@ Codes _readBarcodes( params?.tryRotate ?? true ? 1 : 0, params?.tryInverted ?? false ? 1 : 0, ); - final List results = []; + + final List codes = []; + + 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()); } malloc.free(result.results); - return Codes(codes: results, duration: result.duration); + return Codes(codes: codes, duration: result.duration); } diff --git a/src/native_zxing.cpp b/src/native_zxing.cpp index b4c4ab9..727827e 100644 --- a/src/native_zxing.cpp +++ b/src/native_zxing.cpp @@ -10,42 +10,63 @@ #include #include #include +#include +#include using namespace ZXing; using namespace std; -extern "C" +// Returns an owned C-string `char*`, copied from a `std::string&`. +char* cstrFromString(const std::string& s) { - void resultToCodeResult(struct CodeResult *code, Result result) - { - string text = result.text(); - code->text = new char[text.length() + 1]; - strcpy(code->text, text.c_str()); - - code->isValid = result.isValid(); - - string error = result.error().msg(); - code->error = new char[error.length() + 1]; - strcpy(code->error, error.c_str()); - - code->format = static_cast(result.format()); + auto size = s.length() + 1; + char* out = new char[size]; + std::copy(s.begin(), s.end(), out); + out[size] = '\0'; + return out; +} - auto length = result.bytes().size(); - auto* bytes = new uint8_t[length]; - std::copy(result.bytes().begin(), result.bytes().end(), bytes); - code->bytes = bytes; - code->length = length; +// Returns an owned byte buffer `uint8_t*`, copied from a +// `std::vector&`. +uint8_t* bytesFromVector(const std::vector& v) +{ + auto* bytes = new uint8_t[v.size()]; + std::copy(v.begin(), v.end(), bytes); + return bytes; +} - auto p = result.position(); - auto tl = p.topLeft(); - auto tr = p.topRight(); - auto bl = p.bottomLeft(); - auto br = p.bottomRight(); - code->pos = Pos{0, 0, tl.x, tl.y, tr.x, tr.y, bl.x, bl.y, br.x, br.y}; +// Construct a `CodeResult` from a zxing barcode decode `Result` from within an +// image. +CodeResult codeResultFromResult( + const Result& result, + int duration, + int width, + int height +) { + auto p = result.position(); + auto tl = p.topLeft(); + auto tr = p.topRight(); + auto bl = p.bottomLeft(); + auto br = p.bottomRight(); + + const auto text = result.text(); + + struct CodeResult code {}; + code.text = cstrFromString(text); + code.isValid = result.isValid(); + code.error = cstrFromString(result.error().msg()); + code.bytes = bytesFromVector(result.bytes()); + code.length = static_cast(result.bytes().size()); + code.format = static_cast(result.format()); + code.pos = Pos {width, height, tl.x, tl.y, tr.x, tr.y, bl.x, bl.y, br.x, br.y}; + code.isInverted = result.isInverted(); + code.isMirrored = result.isMirrored(); + code.duration = duration; + return code; +} - code->isInverted = result.isInverted(); - code->isMirrored = result.isMirrored(); - } +extern "C" +{ FUNCTION_ATTRIBUTE void setLogEnabled(int enable) @@ -72,17 +93,15 @@ extern "C" } ReaderOptions hints = ReaderOptions().setTryHarder(tryHarder).setTryRotate(tryRotate).setFormats(BarcodeFormat(format)).setTryInvert(tryInvert).setReturnErrors(true); Result result = ReadBarcode(image, hints); + + // Dart passes us an owned image bytes pointer; we need to free it after + // we're done decoding. delete[] bytes; - struct CodeResult code {}; - resultToCodeResult(&code, result); + int duration = static_cast(get_now() - start); + platform_log("Read Barcode in: %d ms\n", duration); - int evalInMillis = static_cast(get_now() - start); - code.duration = evalInMillis; - code.pos.imageWidth = width; - code.pos.imageHeight = height; - platform_log("Read Barcode in: %d ms\n", code.duration); - return code; + return codeResultFromResult(result, duration, width, height); } FUNCTION_ATTRIBUTE @@ -97,25 +116,25 @@ extern "C" } ReaderOptions hints = ReaderOptions().setTryHarder(tryHarder).setTryRotate(tryRotate).setFormats(BarcodeFormat(format)).setTryInvert(tryInvert); Results results = ReadBarcodes(image, hints); + + // Dart passes us an owned image bytes pointer; we need to free it after + // we're done decoding. delete[] bytes; - int evalInMillis = static_cast(get_now() - start); - platform_log("Read Barcode in: %d ms\n", evalInMillis); + int duration = static_cast(get_now() - start); + platform_log("Read Barcode in: %d ms\n", duration); + + if (results.empty()) { + return CodeResults {0, nullptr, duration}; + } - auto *codes = new struct CodeResult[results.size()]; + auto* codes = new struct CodeResult[results.size()]; int i = 0; - for (auto &result : results) - { - struct CodeResult code {}; - resultToCodeResult(&code, result); - code.duration = evalInMillis; - code.pos.imageWidth = width; - code.pos.imageHeight = height; - codes[i] = code; + for (const auto& result : results) { + codes[i] = codeResultFromResult(result, duration, width, height); i++; } - - return {i, codes, evalInMillis}; + return CodeResults {i, codes, duration}; } FUNCTION_ATTRIBUTE From 42a787aa2333886a2a136d9c6f48a77a8ca5031c Mon Sep 17 00:00:00 2001 From: Philip Hayes Date: Wed, 31 Jan 2024 13:00:15 +0800 Subject: [PATCH 6/7] ffi: int -> bool where appropriate --- lib/generated_bindings.dart | 54 +++++++++++++++--------------- lib/src/logic/barcode_reader.dart | 6 ++-- lib/src/logic/barcodes_reader.dart | 6 ++-- lib/src/logic/zxing.dart | 3 +- lib/src/utils/extentions.dart | 8 ++--- src/common.cpp | 2 +- src/common.h | 2 +- src/native_zxing.cpp | 6 ++-- src/native_zxing.h | 17 +++++----- 9 files changed, 52 insertions(+), 52 deletions(-) diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index e3732a1..4a695d3 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -29,19 +29,19 @@ class GeneratedBindings { /// @brief Enables or disables the logging of the library. /// - /// @param enable Whether to enable or disable the logging. + /// @param enabled Whether to enable or disable the logging. void setLogEnabled( - int enable, + bool enabled, ) { return _setLogEnabled( - enable, + enabled, ); } late final _setLogEnabledPtr = - _lookup>('setLogEnabled'); + _lookup>('setLogEnabled'); late final _setLogEnabled = - _setLogEnabledPtr.asFunction(); + _setLogEnabledPtr.asFunction(); /// Returns the version of the zxing-cpp library. Pointer has a static lifetime and must not be freed. /// @@ -74,9 +74,9 @@ class GeneratedBindings { int height, int cropWidth, int cropHeight, - int tryHarder, - int tryRotate, - int tryInvert, + bool tryHarder, + bool tryRotate, + bool tryInvert, ) { return _readBarcode( bytes, @@ -102,12 +102,12 @@ class GeneratedBindings { 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, int, int, int, int, int, int, - int, int, int)>(); + bool, bool, bool)>(); /// @brief Read barcodes from image bytes. /// @param bytes Image bytes. Owned pointer. Will be freed by native code. @@ -128,9 +128,9 @@ class GeneratedBindings { int height, int cropWidth, int cropHeight, - int tryHarder, - int tryRotate, - int tryInvert, + bool tryHarder, + bool tryRotate, + bool tryInvert, ) { return _readBarcodes( bytes, @@ -156,12 +156,12 @@ class GeneratedBindings { 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, int, int, int, int, int, int, - int, int, int)>(); + bool, bool, bool)>(); /// @brief Encode a string into a barcode /// @param contents The string to encode. Owned pointer. Will be freed by native code. @@ -246,8 +246,8 @@ final class CodeResult extends ffi.Struct { external ffi.Pointer text; /// < Whether the barcode was successfully decoded - @ffi.Int() - external int isValid; + @ffi.Bool() + external bool isValid; /// < The error message. Owned pointer. Must be freed by Dart code if not null. external ffi.Pointer error; @@ -267,12 +267,12 @@ final class CodeResult extends ffi.Struct { 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() @@ -296,8 +296,8 @@ 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. Owned pointer. Must be freed by Dart code if not null. external ffi.Pointer text; diff --git a/lib/src/logic/barcode_reader.dart b/lib/src/logic/barcode_reader.dart index c7dadb6..1c0fd22 100644 --- a/lib/src/logic/barcode_reader.dart +++ b/lib/src/logic/barcode_reader.dart @@ -74,9 +74,9 @@ Code _readBarcode( 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(); } diff --git a/lib/src/logic/barcodes_reader.dart b/lib/src/logic/barcodes_reader.dart index 735241c..d76bdb0 100644 --- a/lib/src/logic/barcodes_reader.dart +++ b/lib/src/logic/barcodes_reader.dart @@ -73,9 +73,9 @@ Codes _readBarcodes( 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 codes = []; diff --git a/lib/src/logic/zxing.dart b/lib/src/logic/zxing.dart index 2b695f0..384c78f 100644 --- a/lib/src/logic/zxing.dart +++ b/lib/src/logic/zxing.dart @@ -27,8 +27,7 @@ part 'camera_stream.dart'; String zxingVersion() => bindings.version().cast().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'; diff --git a/lib/src/utils/extentions.dart b/lib/src/utils/extentions.dart index c0a5fa3..8aadbd8 100644 --- a/lib/src/utils/extentions.dart +++ b/lib/src/utils/extentions.dart @@ -50,13 +50,13 @@ extension CodeExt on CodeResult { Code toCode() { return Code( text: copyStringFromOwnedFfiPtr(text), - isValid: isValid == 1, + isValid: isValid, error: copyStringFromOwnedFfiPtr(error), rawBytes: copyUint8ListFromOwnedFfiPtr(bytes, length), format: format, position: pos.toPosition(), - isInverted: isInverted == 1, - isMirrored: isMirrored == 1, + isInverted: isInverted, + isMirrored: isMirrored, duration: duration, ); } @@ -64,7 +64,7 @@ extension CodeExt on CodeResult { extension EncodeExt on EncodeResult { Encode toEncode() => Encode( - isValid == 1, + isValid, format, copyStringFromOwnedFfiPtr(text), copyUint32ListFromOwnedFfiPtr(data, length), diff --git a/src/common.cpp b/src/common.cpp index 4fffec9..8d8985c 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -38,4 +38,4 @@ void platform_log(const char *fmt, ...) #endif va_end(args); } -} \ No newline at end of file +} diff --git a/src/common.h b/src/common.h index a4cb7b8..7fe3610 100644 --- a/src/common.h +++ b/src/common.h @@ -23,4 +23,4 @@ long long int get_now(); void platform_log(const char *fmt, ...); -void setLoggingEnabled(bool enabled); \ No newline at end of file +void setLoggingEnabled(bool enabled); diff --git a/src/native_zxing.cpp b/src/native_zxing.cpp index 727827e..06d0614 100644 --- a/src/native_zxing.cpp +++ b/src/native_zxing.cpp @@ -69,7 +69,7 @@ extern "C" { FUNCTION_ATTRIBUTE - void setLogEnabled(int enable) + void setLogEnabled(bool enable) { setLoggingEnabled(enable); } @@ -82,7 +82,7 @@ extern "C" } FUNCTION_ATTRIBUTE - struct CodeResult readBarcode(uint8_t* bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, int tryHarder, int tryRotate, int tryInvert) + struct CodeResult readBarcode(uint8_t* bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, bool tryHarder, bool tryRotate, bool tryInvert) { long long start = get_now(); @@ -105,7 +105,7 @@ extern "C" } FUNCTION_ATTRIBUTE - struct CodeResults readBarcodes(uint8_t* bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, int tryHarder, int tryRotate, int tryInvert) + struct CodeResults readBarcodes(uint8_t* bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, bool tryHarder, bool tryRotate, bool tryInvert) { long long start = get_now(); diff --git a/src/native_zxing.h b/src/native_zxing.h index 9d5d1e3..bbf28d0 100644 --- a/src/native_zxing.h +++ b/src/native_zxing.h @@ -1,6 +1,7 @@ #ifdef __cplusplus #include #else + #include #include #endif @@ -33,14 +34,14 @@ extern "C" struct CodeResult { char *text; ///< The decoded text. Owned pointer. Must be freed by Dart code if not null. - int isValid; ///< Whether the barcode was successfully decoded + bool isValid; ///< Whether the barcode was successfully decoded char *error; ///< The error message. Owned pointer. Must be freed by Dart code if not null. uint8_t* bytes; ///< The bytes is the raw content without any character set conversions. Owned pointer. Must be freed by Dart code if not null. int length; ///< The length of the bytes int format; ///< The format of the barcode struct Pos pos; ///< The position of the barcode within the image - int isInverted; ///< Whether the barcode was inverted - int isMirrored; ///< Whether the barcode was mirrored + bool isInverted; ///< Whether the barcode was inverted + bool isMirrored; ///< Whether the barcode was mirrored int duration; ///< The duration of the decoding in milliseconds }; @@ -60,7 +61,7 @@ extern "C" */ struct EncodeResult { - int isValid; ///< Whether the barcode was successfully encoded + bool isValid; ///< Whether the barcode was successfully encoded char *text; ///< The encoded text. Owned pointer. Must be freed by Dart code if not null. int format; ///< The format of the barcode uint8_t* data; ///< The encoded data. Owned pointer. Must be freed by Dart code if not null. @@ -71,9 +72,9 @@ extern "C" /** * @brief Enables or disables the logging of the library. * - * @param enable Whether to enable or disable the logging. + * @param enabled Whether to enable or disable the logging. */ - void setLogEnabled(int enable); + void setLogEnabled(bool enabled); /** * Returns the version of the zxing-cpp library. Pointer has a static lifetime and must not be freed. @@ -95,7 +96,7 @@ extern "C" * @param tryRotate Also try detecting code in 90, 180 and 270 degree rotated images. * @return The barcode result. */ - struct CodeResult readBarcode(uint8_t* bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, int tryHarder, int tryRotate, int tryInvert); + struct CodeResult readBarcode(uint8_t* bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, bool tryHarder, bool tryRotate, bool tryInvert); /** * @brief Read barcodes from image bytes. @@ -110,7 +111,7 @@ extern "C" * @param tryRotate Also try detecting code in 90, 180 and 270 degree rotated images. * @return The barcode results. */ - struct CodeResults readBarcodes(uint8_t* bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, int tryHarder, int tryRotate, int tryInvert); + struct CodeResults readBarcodes(uint8_t* bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, bool tryHarder, bool tryRotate, bool tryInvert); /** * @brief Encode a string into a barcode From c1ffa3bd4a774e5007f4ca20d07da71cd08f0b25 Mon Sep 17 00:00:00 2001 From: Philip Hayes Date: Wed, 31 Jan 2024 13:33:23 +0800 Subject: [PATCH 7/7] ffi: should use monotonic clock to measure durations --- src/common.cpp | 8 -------- src/common.h | 2 -- src/native_zxing.cpp | 24 +++++++++++++++++------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index 8d8985c..4140055 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1,5 +1,4 @@ #include "common.h" -#include #include #include @@ -12,13 +11,6 @@ void setLoggingEnabled(bool enabled) isLogEnabled = enabled; } -long long int get_now() -{ - return chrono::duration_cast( - chrono::system_clock::now().time_since_epoch()) - .count(); -} - void platform_log(const char *fmt, ...) { if (isLogEnabled) diff --git a/src/common.h b/src/common.h index 7fe3610..7d40136 100644 --- a/src/common.h +++ b/src/common.h @@ -19,8 +19,6 @@ #define FUNCTION_ATTRIBUTE __declspec(dllexport) #endif -long long int get_now(); - void platform_log(const char *fmt, ...); void setLoggingEnabled(bool enabled); diff --git a/src/native_zxing.cpp b/src/native_zxing.cpp index 06d0614..6ef6568 100644 --- a/src/native_zxing.cpp +++ b/src/native_zxing.cpp @@ -6,6 +6,7 @@ // #include "ZXVersion.h" // This file is not existing for iOS #include +#include #include #include #include @@ -15,6 +16,7 @@ using namespace ZXing; using namespace std; +using std::chrono::steady_clock; // Returns an owned C-string `char*`, copied from a `std::string&`. char* cstrFromString(const std::string& s) @@ -65,6 +67,14 @@ CodeResult codeResultFromResult( return code; } +// Returns the duration elapsed in milliseconds since `start`. +int elapsed_ms(const steady_clock::time_point& start) +{ + auto end = steady_clock::now(); + auto duration = end - start; + return chrono::duration_cast(duration).count(); +} + extern "C" { @@ -84,7 +94,7 @@ extern "C" FUNCTION_ATTRIBUTE struct CodeResult readBarcode(uint8_t* bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, bool tryHarder, bool tryRotate, bool tryInvert) { - long long start = get_now(); + auto start = steady_clock::now(); ImageView image {bytes, width, height, ImageFormat(imageFormat)}; if (cropWidth > 0 && cropHeight > 0 && cropWidth < width && cropHeight < height) @@ -98,7 +108,7 @@ extern "C" // we're done decoding. delete[] bytes; - int duration = static_cast(get_now() - start); + int duration = elapsed_ms(start); platform_log("Read Barcode in: %d ms\n", duration); return codeResultFromResult(result, duration, width, height); @@ -107,7 +117,7 @@ extern "C" FUNCTION_ATTRIBUTE struct CodeResults readBarcodes(uint8_t* bytes, int imageFormat, int format, int width, int height, int cropWidth, int cropHeight, bool tryHarder, bool tryRotate, bool tryInvert) { - long long start = get_now(); + auto start = steady_clock::now(); ImageView image{bytes, width, height, ImageFormat(imageFormat)}; if (cropWidth > 0 && cropHeight > 0 && cropWidth < width && cropHeight < height) @@ -121,7 +131,7 @@ extern "C" // we're done decoding. delete[] bytes; - int duration = static_cast(get_now() - start); + int duration = elapsed_ms(start); platform_log("Read Barcode in: %d ms\n", duration); if (results.empty()) { @@ -140,7 +150,7 @@ extern "C" FUNCTION_ATTRIBUTE struct EncodeResult encodeBarcode(char* contents, int width, int height, int format, int margin, int eccLevel) { - long long start = get_now(); + auto start = steady_clock::now(); struct EncodeResult result = {0, contents, format, nullptr, 0, nullptr}; try @@ -166,8 +176,8 @@ extern "C" strcpy(result.error, e.what()); } - int evalInMillis = static_cast(get_now() - start); - platform_log("Encode Barcode in: %d ms\n", evalInMillis); + int duration = elapsed_ms(start); + platform_log("Encode Barcode in: %d ms\n", duration); return result; } }