diff --git a/packages/isar/lib/isar.dart b/packages/isar/lib/isar.dart index 429086e5f..5c4c77b4e 100644 --- a/packages/isar/lib/isar.dart +++ b/packages/isar/lib/isar.dart @@ -17,6 +17,11 @@ import 'package:isar/src/native/split_words.dart' import 'package:meta/meta.dart'; import 'package:meta/meta_meta.dart'; +export 'dart:convert'; + +part 'src/xxh3/xxh3.dart'; +part 'src/xxh3/math.dart'; +part 'src/xxh3/util.dart'; part 'src/annotations/backlink.dart'; part 'src/annotations/collection.dart'; part 'src/annotations/embedded.dart'; diff --git a/packages/isar/lib/src/native/bindings.dart b/packages/isar/lib/src/native/bindings.dart index 734bc9d1f..bfe7b2b4a 100644 --- a/packages/isar/lib/src/native/bindings.dart +++ b/packages/isar/lib/src/native/bindings.dart @@ -2024,8 +2024,8 @@ class IsarCoreBindings { } late final _isar_q_aggregate_long_resultPtr = _lookup< - ffi.NativeFunction< - ffi.Int64 Function(ffi.Pointer)>>( + ffi + .NativeFunction)>>( 'isar_q_aggregate_long_result'); late final _isar_q_aggregate_long_result = _isar_q_aggregate_long_resultPtr .asFunction)>(); @@ -2088,9 +2088,9 @@ class IsarCoreBindings { } late final _isar_txn_finishPtr = _lookup< - ffi.NativeFunction< - ffi.Int64 Function( - ffi.Pointer, ffi.Bool)>>('isar_txn_finish'); + ffi + .NativeFunction, ffi.Bool)>>( + 'isar_txn_finish'); late final _isar_txn_finish = _isar_txn_finishPtr .asFunction, bool)>(); diff --git a/packages/isar/lib/src/schema/collection_schema.dart b/packages/isar/lib/src/schema/collection_schema.dart index 24d280a19..71a76c9ca 100644 --- a/packages/isar/lib/src/schema/collection_schema.dart +++ b/packages/isar/lib/src/schema/collection_schema.dart @@ -5,7 +5,7 @@ class CollectionSchema extends Schema { /// @nodoc @protected const CollectionSchema({ - required super.id, + required super.idGenerator, required super.name, required super.properties, required super.estimateSize, @@ -31,7 +31,7 @@ class CollectionSchema extends Schema { factory CollectionSchema.fromJson(Map json) { final collection = Schema.fromJson(json); return CollectionSchema( - id: collection.id, + idGenerator: collection.idGenerator, name: collection.name, properties: collection.properties, idName: json['idName'] as String, diff --git a/packages/isar/lib/src/schema/index_schema.dart b/packages/isar/lib/src/schema/index_schema.dart index d8ceb8b9c..6e4f4bad8 100644 --- a/packages/isar/lib/src/schema/index_schema.dart +++ b/packages/isar/lib/src/schema/index_schema.dart @@ -5,7 +5,7 @@ class IndexSchema { /// @nodoc @protected const IndexSchema({ - required this.id, + required this.idGenerator, required this.name, required this.unique, required this.replace, @@ -16,7 +16,7 @@ class IndexSchema { @protected factory IndexSchema.fromJson(Map json) { return IndexSchema( - id: -1, + idGenerator: () => -1, name: json['name'] as String, unique: json['unique'] as bool, replace: json['replace'] as bool, @@ -27,7 +27,9 @@ class IndexSchema { } /// Internal id of this index. - final int id; + int get id => idGenerator(); + + final int Function() idGenerator; /// Name of this index. final String name; diff --git a/packages/isar/lib/src/schema/link_schema.dart b/packages/isar/lib/src/schema/link_schema.dart index 046ed6456..786e96a45 100644 --- a/packages/isar/lib/src/schema/link_schema.dart +++ b/packages/isar/lib/src/schema/link_schema.dart @@ -5,7 +5,7 @@ class LinkSchema { /// @nodoc @protected const LinkSchema({ - required this.id, + required this.idGenerator, required this.name, required this.target, required this.single, @@ -16,7 +16,7 @@ class LinkSchema { @protected factory LinkSchema.fromJson(Map json) { return LinkSchema( - id: -1, + idGenerator: () => -1, name: json['name'] as String, target: json['target'] as String, single: json['single'] as bool, @@ -25,7 +25,9 @@ class LinkSchema { } /// Internal id of this link. - final int id; + int get id => idGenerator(); + + final int Function() idGenerator; /// Name of this link. final String name; diff --git a/packages/isar/lib/src/schema/schema.dart b/packages/isar/lib/src/schema/schema.dart index 8cdf73a8b..3ab293fe6 100644 --- a/packages/isar/lib/src/schema/schema.dart +++ b/packages/isar/lib/src/schema/schema.dart @@ -5,7 +5,7 @@ class Schema { /// @nodoc @protected const Schema({ - required this.id, + required this.idGenerator, required this.name, required this.properties, required this.estimateSize, @@ -18,7 +18,7 @@ class Schema { @protected factory Schema.fromJson(Map json) { return Schema( - id: -1, + idGenerator: () => -1, name: json['name'] as String, properties: { for (final property in json['properties'] as List) @@ -33,7 +33,9 @@ class Schema { } /// Internal id of this collection or embedded object. - final int id; + int get id => idGenerator(); + + final int Function() idGenerator; /// Name of the collection or embedded object final String name; diff --git a/packages/isar/lib/src/xxh3/math.dart b/packages/isar/lib/src/xxh3/math.dart new file mode 100644 index 000000000..137964968 --- /dev/null +++ b/packages/isar/lib/src/xxh3/math.dart @@ -0,0 +1,312 @@ +// ignore_for_file: public_member_api_docs, unnecessary_parenthesis +// ignore_for_file: constant_identifier_names + +part of isar; + +const int kXXHPrime32_1 = 0x9E3779B1; +const int kXXHPrime32_2 = 0x85EBCA77; +const int kXXHPrime32_3 = 0xC2B2AE3D; + +const int kXXHPrime64_1_HIGH = 0x9E3779B1; +const int kXXHPrime64_1_LOW = 0x85EBCA87; +const int kXXHPrime64_2_HIGH = 0xC2B2AE3D; +const int kXXHPrime64_2_LOW = 0x27D4EB4F; +const int kXXHPrime64_3_HIGH = 0x165667B1; +const int kXXHPrime64_3_LOW = 0x9E3779F9; +const int kXXHPrime64_4_HIGH = 0x85EBCA77; +const int kXXHPrime64_4_LOW = 0xC2B2AE63; +const int kXXHPrime64_5_HIGH = 0x27D4EB2F; +const int kXXHPrime64_5_LOW = 0x165667C5; + +int mix64(int high, int low) { + // Combine high and low parts using bitwise operations + return (high * 0x100000000) + low; +} + +/// The number of secret bytes consumed at each accumulation. +const int kSecretConsumeRate = 8; +const int kStripeLength = 64; +const int kAccNB = 8; // = kStripeLength ~/ sizeof(uint64_t) + +const int kXXH3MidSizeMax = 240; + +/// The default pseudo-random secret value for an XXH3 hash, originally +/// taken from FARSH. +final kSecret = Uint8List.fromList([ + 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, // + 0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, // + 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, 0xcb, 0x79, 0xe6, 0x4e, // + 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, // + 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, // + 0x81, 0x3a, 0x26, 0x4c, 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, // + 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, 0x71, 0x64, 0x48, 0x97, // + 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, // + 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, // + 0xc7, 0x0b, 0x4f, 0x1d, 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, // + 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, 0xea, 0xc5, 0xac, 0x83, // + 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, // + 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, // + 0x29, 0xd4, 0x68, 0x9e, 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, // + 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, 0x45, 0xcb, 0x3a, 0x8f, // + 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, // +]); + +/// A dart implementation of xxh64_avalanche. +/// Mixes all the bits to finalize the hash. +/// The final mix ensures the input bits have all had a chance to impact any +/// bit in the output digest thereby removing bias from the distribution. +int _xXH64Avalanche(int h) { + h ^= h >>> 33; + h *= mix64(kXXHPrime64_2_HIGH, kXXHPrime64_2_LOW); + h ^= h >>> 29; + h *= mix64(kXXHPrime64_3_HIGH, kXXHPrime64_3_LOW); + return h ^ (h >>> 32); +} + +/// Dart implementation of the xxh3_avalanche. +/// A fast avalanche stage for when input bits have been already partially +/// mixed. +int _xXH3Avalanche(int h) { + h ^= h >>> 37; + h *= mix64(0x16566791, 0x9E3779F9); + return h ^ (h >>> 32); +} + +/// Dart implementation of the xxh3_rrmxmx. +/// Based on Pelle Evensen's rrmxmx and preferred when input has not been +/// mixed. +int _xXH3rrmxmx(int h, int length) { + h ^= ((h << 49) | (h >>> 15)) ^ ((h << 24) | (h >>> 40)); + h *= mix64(0x9FB21C65, 0x1E98DF25); + h ^= (h >>> 35) + length; + h *= mix64(0x9FB21C65, 0x1E98DF25); + return h ^ (h >>> 28); +} + +/// Dart implementation of the xxh3_mix16b hash function from XXH3. +int _xXH3Mix16B( + Uint8List input, + Uint8List secret, + int seed, { + int inputOffset = 0, + int secretOffset = 0, +}) { + return mul128Fold64( + readLE64(input, inputOffset) ^ (readLE64(secret, secretOffset) + seed), + readLE64(input, inputOffset + 8) ^ + (readLE64(secret, secretOffset + 8) - seed), + ); +} + +/// Dart implementation of the xxh3_accumulate_512 hash function from XXH3. +void _xXH3Accumulate512( + List acc, + Uint8List input, + Uint8List secret, { + int inputOffset = 0, + int secretOffset = 0, +}) { + for (var i = 0; i < kAccNB; i++) { + final dataVal = readLE64(input, inputOffset + (8 * i)); + final dataKey = dataVal ^ readLE64(secret, secretOffset + (i * 8)); + acc[i ^ 1] += dataVal; + acc[i] += dataKey.toUnsigned(32) * (dataKey >>> 32); + } +} + +/// The default [HashLongFunction] for XXH3. +int xXH3HashLong64bInternal(Uint8List input, int seed, Uint8List secret) { + if (seed == 0) { + secret = kSecret; + } else if (secret == kSecret) { + final kSecretDefaultSize = kSecret.lengthInBytes; + final updatedSecret = Uint8List(kSecretDefaultSize); + final secretData = ByteData.sublistView(updatedSecret); + + for (var i = 0; i < kSecretDefaultSize; i += 16) { + secretData.setUint64(i, readLE64(kSecret, i) + seed, Endian.little); + secretData.setUint64( + i + 8, + readLE64(kSecret, i + 8) - seed, + Endian.little, + ); + } + + secret = updatedSecret; + } + + final length = input.lengthInBytes; + final secretLength = secret.lengthInBytes; + + final acc = [ + kXXHPrime32_3, + mix64(kXXHPrime64_1_HIGH, kXXHPrime64_1_LOW), + mix64(kXXHPrime64_2_HIGH, kXXHPrime64_2_LOW), + mix64(kXXHPrime64_3_HIGH, kXXHPrime64_3_LOW), + mix64(kXXHPrime64_4_HIGH, kXXHPrime64_4_LOW), + kXXHPrime32_2, + mix64(kXXHPrime64_5_HIGH, kXXHPrime64_5_LOW), + kXXHPrime32_1, + ]; + final nbStripesPerBlock = + (secretLength - kStripeLength) ~/ kSecretConsumeRate; + final blockLen = kStripeLength * nbStripesPerBlock; + final nbBlocks = (length - 1) ~/ blockLen; + + for (var n = 0; n < nbBlocks; n++) { + for (var i = 0; i < nbStripesPerBlock; i++) { + _xXH3Accumulate512( + acc, + input, + secret, + inputOffset: n * blockLen + i * kStripeLength, + secretOffset: i * kSecretConsumeRate, + ); + } + + for (var i = 0; i < kAccNB; i++) { + acc[i] = (acc[i] ^ + (acc[i] >>> 47) ^ + readLE64(secret, secretLength - kStripeLength + 8 * i)) * + kXXHPrime32_1; + } + } + + final nbStripes = ((length - 1) - (blockLen * nbBlocks)) ~/ kStripeLength; + for (var i = 0; i < nbStripes; i++) { + _xXH3Accumulate512( + acc, + input, + secret, + inputOffset: nbBlocks * blockLen + i * kStripeLength, + secretOffset: i * kSecretConsumeRate, + ); + } + _xXH3Accumulate512( + acc, + input, + secret, + inputOffset: length - kStripeLength, + secretOffset: secretLength - kStripeLength - 7, + ); + var result = length * mix64(kXXHPrime64_1_HIGH, kXXHPrime64_1_LOW); + for (var i = 0; i < 4; i++) { + result += mul128Fold64( + acc[2 * i] ^ readLE64(secret, 11 + 16 * i), + acc[2 * i + 1] ^ readLE64(secret, 11 + 16 * i + 8), + ); + } + return _xXH3Avalanche(result); +} + +/// The internal entry point for the 64-bit variant of the XXH3 hash. +int xXH3_64bitsInternal({ + required Uint8List input, + required int seed, + required Uint8List secret, + required HashLongFunction hashLongFunction, +}) { + if (secret.lengthInBytes < kSecretSizeMin) { + throw ArgumentError.value( + secret, + 'secret', + 'The specified secret is too short. It must be at least $kSecretSizeMin bytes.', + ); + } + + final length = input.lengthInBytes; + + // Refer to XXH3_64bits_withSecretAndSeed, notice that if the seed is not + // the default and the length is less than the midSizeMax, a custom secret + // will be ignored. + if (seed != 0 && length <= kXXH3MidSizeMax && secret != kSecret) { + secret = kSecret; + // The original source code also specifies hashLong as NULL, I'm assuming + // that's because it would never be used with the length being less than + // kXXH3MidSizeMax, rather than as a preventative measure. + } + + if (length == 0) { + return _xXH64Avalanche( + seed ^ (readLE64(secret, 56) ^ readLE64(secret, 64)), + ); + } else if (length < 4) { + final keyed = ((((input[0])) << 16) | + (((input[length >>> 1])) << 24) | + input[length - 1] | + ((length) << 8)) ^ + ((readLE32(secret) ^ readLE32(secret, 4)) + seed); + + return _xXH64Avalanche(keyed); + } else if (length <= 8) { + final keyed = (readLE32(input, length - 4) + ((readLE32(input)) << 32)) ^ + ((readLE64(secret, 8) ^ readLE64(secret, 16)) - + (seed ^ ((swap32((seed))) << 32))); + return _xXH3rrmxmx(keyed, length); + } else if (length <= 16) { + final inputLo = readLE64(input) ^ + ((readLE64(secret, 24) ^ readLE64(secret, 32)) + seed); + final inputHi = readLE64(input, length - 8) ^ + ((readLE64(secret, 40) ^ readLE64(secret, 48)) - seed); + final acc = + length + swap64(inputLo) + inputHi + mul128Fold64(inputLo, inputHi); + return _xXH3Avalanche(acc); + } else if (length <= 128) { + var acc = length * mix64(kXXHPrime64_1_HIGH, kXXHPrime64_1_LOW); + var secretOff = 0; + for (var i = 0, j = length; j > i; i += 16, j -= 16) { + acc += _xXH3Mix16B( + input, + secret, + seed, + inputOffset: i, + secretOffset: secretOff, + ); + acc += _xXH3Mix16B( + input, + secret, + seed, + inputOffset: j - 16, + secretOffset: secretOff + 16, + ); + secretOff += 32; + } + return _xXH3Avalanche(acc); + } else if (length <= 240) { + var acc = length * mix64(kXXHPrime64_1_HIGH, kXXHPrime64_1_LOW); + final nbRounds = length ~/ 16; + + var i = 0; + for (; i < 8; ++i) { + acc += _xXH3Mix16B( + input, + secret, + seed, + inputOffset: 16 * i, + secretOffset: 16 * i, + ); + } + acc = _xXH3Avalanche(acc); + + for (; i < nbRounds; ++i) { + acc += _xXH3Mix16B( + input, + secret, + seed, + inputOffset: 16 * i, + secretOffset: 16 * (i - 8) + 3, + ); + } + + acc += _xXH3Mix16B( + input, + secret, + seed, + inputOffset: length - 16, + secretOffset: kSecretSizeMin - 17, + ); + return _xXH3Avalanche(acc); + } else { + return hashLongFunction(input, seed, secret); + } +} diff --git a/packages/isar/lib/src/xxh3/util.dart b/packages/isar/lib/src/xxh3/util.dart new file mode 100644 index 000000000..8a3adc23f --- /dev/null +++ b/packages/isar/lib/src/xxh3/util.dart @@ -0,0 +1,75 @@ +// ignore_for_file: public_member_api_docs +// ignore_for_file: constant_identifier_names + +part of isar; + +// +// Buffers +// + +/// Reads a 32-bit little-endian integer from the specified buffer at the +/// specified byte offset. (If unspecified, [byteOffset] is 0 which means the +// /// value is read from the start of the buffer.) +int readLE32(Uint8List value, [int byteOffset = 0]) { + return ByteData.sublistView(value).getUint32(byteOffset, Endian.little); +} + +/// Reads a 64-bit little-endian integer from the specified buffer at the +/// specified byte offset. (If unspecified, [byteOffset] is 0 which means the +/// value is read from the start of the buffer.) +int readLE64(Uint8List value, [int byteOffset = 0]) { + return ByteData.sublistView(value).getUint64(byteOffset, Endian.little); +} + +// +// Math +// + +/// Holds a pair of integers, aims to be analogous to an std::pair in C++. +/// Used to represent a 128-bit integer. +class PairInt { + PairInt(this.lhs, this.rhs); + int lhs; + int rhs; +} + +/// Multiplies the 64-bit integers stored in [lhs] and [rhs] (bitwise) and +/// stores the result in a 128-bit integer in the form of a [PairInt]. +PairInt mult64to128(int lhs, int rhs) { + final loLo = (lhs.toUnsigned(32) * rhs.toUnsigned(32)).toUnsigned(64); + final hiLo = (lhs >>> 32) * rhs.toUnsigned(32); + final loHi = lhs.toUnsigned(32) * (rhs >>> 32); + final hiHi = (lhs >>> 32) * (rhs >>> 32); + final cross = (loLo >>> 32) + hiLo.toUnsigned(32) + loHi; + final upper = (hiLo >>> 32) + (cross >>> 32) + hiHi; + final lower = (cross << 32) | loLo.toUnsigned(32); + return PairInt(lower, upper); +} + +/// Multiplies [lhs] and [rhs], bitwise, storing the result in a 128-bit +/// integer with [mult64to128], then XORs the resulting pair of 64-bit integers +/// to 'fold' it back into a 64-bit integer. +int mul128Fold64(int lhs, int rhs) { + final product = mult64to128(lhs, rhs); + return product.lhs ^ product.rhs; +} + +/// Swaps the byte order of a 32-bit integer. +int swap32(int x) { + return ((x << 24) & 0xff000000) | + ((x << 8) & 0x00ff0000) | + ((x >>> 8) & 0x0000ff00) | + ((x >>> 24) & 0x000000ff); +} + +/// Swaps the byte order of a 64-bit integer. +int swap64(int x) { + return ((x << 56) & 0xff00000000000000) | + ((x << 40) & 0x00ff000000000000) | + ((x << 24) & 0x0000ff0000000000) | + ((x << 8) & 0x000000ff00000000) | + ((x >>> 8) & 0x00000000ff000000) | + ((x >>> 24) & 0x0000000000ff0000) | + ((x >>> 40) & 0x000000000000ff00) | + ((x >>> 56) & 0x00000000000000ff); +} diff --git a/packages/isar/lib/src/xxh3/xxh3.dart b/packages/isar/lib/src/xxh3/xxh3.dart new file mode 100644 index 000000000..d0b790d81 --- /dev/null +++ b/packages/isar/lib/src/xxh3/xxh3.dart @@ -0,0 +1,51 @@ +part of isar; + +/// The bare minimum size for a custom secret as defined in XXH3. +/// See https://github.com/Cyan4973/xxHash/blob/b1a61dff654af43552b5ee05c737b6fd2a0ee14b/xxhash.h#L931 +const kSecretSizeMin = 136; + +/// When hashing inputs of length greater than 240, the [HashLongFunction] +/// is used. The default is [kXXH3HashLongFunction64Bit]. +typedef HashLongFunction = int Function( + Uint8List input, int seed, Uint8List secret); + +/// The default HashLongFunction from xxHash. +/// See [HashLongFunction]. +const HashLongFunction kXXH3HashLongFunction64Bit = xXH3HashLong64bInternal; + +/// Perform an XXH3 hash of the input data. +/// The input data is provided as a [Uint8List] to avoid having to +/// perform conversions internally. +/// +/// Optionally, a [secret] may be specified (if none is specified, +/// the default seed is used). If a secret is specified, it must be +/// greater in length than [kSecretSizeMin]. +/// +/// Per XXH3, the secret **MUST** look like a bunch of random bytes as +/// the quality of the secret impacts the dispersion of the hash algorithm. +/// "Trivial" or structured data such as repeated sequences or a text +/// document should be avoided. +/// +/// The [seed] may be customized (the default value of the seed is 0). +/// +/// A custom [HashLongFunction] may also be specified. By default, +/// this is the [kXXH3HashLongFunction64Bit] which is the HashLongFunction +/// included with xxHash. +/// +/// To convert a [List] of [int]s to a [Uint8List], you can use +/// [Uint8List.fromList]. +/// +/// The resulting 64-bit value is returned as an [int]. +int xxh3( + Uint8List input, { + Uint8List? secret, + int seed = 0, + HashLongFunction hashLongFunction = kXXH3HashLongFunction64Bit, +}) { + return xXH3_64bitsInternal( + input: input, + seed: seed, + secret: secret ?? kSecret, + hashLongFunction: hashLongFunction, + ); +} diff --git a/packages/isar/pubspec.yaml b/packages/isar/pubspec.yaml index c5671f7cb..8656c9378 100644 --- a/packages/isar/pubspec.yaml +++ b/packages/isar/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: js: any meta: ^1.7.0 + dev_dependencies: ffigen: "^7.0.0" test: ^1.21.1 diff --git a/packages/isar_generator/lib/src/code_gen/collection_schema_generator.dart b/packages/isar_generator/lib/src/code_gen/collection_schema_generator.dart index 61a23109b..5998cab1c 100644 --- a/packages/isar_generator/lib/src/code_gen/collection_schema_generator.dart +++ b/packages/isar_generator/lib/src/code_gen/collection_schema_generator.dart @@ -20,7 +20,7 @@ String generateSchema(ObjectInfo object) { code += ''' name: r'${object.isarName}', - id: ${object.id}, + idGenerator: ${object.generateIdName}, properties: {$properties}, estimateSize: ${object.estimateSizeName}, @@ -88,7 +88,7 @@ String _generateIndexSchema(ObjectIndex index) { return ''' IndexSchema( - id: ${index.id}, + idGenerator: ${index.id}, name: r'${index.name}', unique: ${index.unique}, replace: ${index.replace}, diff --git a/packages/isar_generator/lib/src/code_gen/type_adapter_generator.dart b/packages/isar_generator/lib/src/code_gen/type_adapter_generator.dart index 16f7d5641..960bf080c 100644 --- a/packages/isar_generator/lib/src/code_gen/type_adapter_generator.dart +++ b/packages/isar_generator/lib/src/code_gen/type_adapter_generator.dart @@ -406,6 +406,14 @@ String _deserialize(ObjectProperty property, String propertyOffset) { } } +String generateGenerateId(ObjectInfo object) { + return ''' + int ${object.generateIdName}() { + return xxh3(utf8.encode(r'${object.isarName}')); + } + '''; +} + String generateGetId(ObjectInfo object) { final defaultVal = object.idProperty.nullable ? '?? Isar.autoIncrement' : ''; return ''' diff --git a/packages/isar_generator/lib/src/collection_generator.dart b/packages/isar_generator/lib/src/collection_generator.dart index 9231281bc..fcb3ae720 100644 --- a/packages/isar_generator/lib/src/collection_generator.dart +++ b/packages/isar_generator/lib/src/collection_generator.dart @@ -61,6 +61,7 @@ class IsarCollectionGenerator extends GeneratorForAnnotation { ${generateEnumMaps(object)} + ${generateGenerateId(object)} ${generateGetId(object)} ${generateGetLinks(object)} ${generateAttach(object)} @@ -91,6 +92,7 @@ class IsarEmbeddedGenerator extends GeneratorForAnnotation { ${generateSchema(object)} + ${generateGenerateId(object)} ${generateEstimateSerialize(object)} ${generateSerialize(object)} ${generateDeserialize(object)} diff --git a/packages/isar_generator/lib/src/object_info.dart b/packages/isar_generator/lib/src/object_info.dart index dff34e501..7886ba2fd 100644 --- a/packages/isar_generator/lib/src/object_info.dart +++ b/packages/isar_generator/lib/src/object_info.dart @@ -4,8 +4,6 @@ import 'dart:typed_data'; import 'package:dartx/dartx.dart'; import 'package:isar/isar.dart'; -import 'package:xxh3/xxh3.dart'; - class ObjectInfo { ObjectInfo({ required this.dartName, @@ -27,8 +25,6 @@ class ObjectInfo { final List indexes; final List links; - int get id => xxh3(utf8.encode(isarName) as Uint8List); - bool get isEmbedded => accessor == null; ObjectProperty get idProperty => properties.firstWhere((it) => it.isId); @@ -36,6 +32,7 @@ class ObjectInfo { List get objectProperties => properties.where((it) => !it.isId).toList(); + String get generateIdName => '_${dartName.decapitalize()}GenerateId'; String get getIdName => '_${dartName.decapitalize()}GetId'; String get getLinksName => '_${dartName.decapitalize()}GetLinks'; String get attachName => '_${dartName.decapitalize()}Attach'; @@ -177,7 +174,7 @@ class ObjectIndex { final bool unique; final bool replace; - late final id = xxh3(utf8.encode(name) as Uint8List); + int id() => xxh3(utf8.encode(name)); } class ObjectLink { @@ -203,9 +200,9 @@ class ObjectLink { int id(String objectIsarName) { final col = isBacklink ? targetCollectionIsarName : objectIsarName; - final colId = xxh3(utf8.encode(col) as Uint8List, seed: isBacklink ? 1 : 0); + final colId = xxh3(utf8.encode(col), seed: isBacklink ? 1 : 0); final name = targetLinkIsarName ?? isarName; - return xxh3(utf8.encode(name) as Uint8List, seed: colId); + return xxh3(utf8.encode(name), seed: colId); } } diff --git a/packages/isar_generator/pubspec.yaml b/packages/isar_generator/pubspec.yaml index f2aeb6c88..97a8655f6 100644 --- a/packages/isar_generator/pubspec.yaml +++ b/packages/isar_generator/pubspec.yaml @@ -19,7 +19,6 @@ dependencies: hosted: https://pub.isar-community.dev path: ^1.8.1 source_gen: ^1.2.2 - xxh3: ^1.0.1 dev_dependencies: build_test: ^2.1.5