iscc-lib 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/iscc_lib/version.rb +1 -1
- data/lib/iscc_lib.rb +18 -0
- data/src/lib.rs +73 -15
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 50fc883dc45f0160658cce96aca449059dcd83f1cd5a344c6254659e0214370c
|
|
4
|
+
data.tar.gz: cde84cb2f3bff34d8c0ae0bdeda3d2064dc852e3055fcfb2e3dff207240a5848
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b96e334c9c24acd5261bdc5d619f7eb92f606a496fe2b59389e76e4c6da0bfa3775969e48d42253921300d7cd3290725fa9975bdb69087cfed0eabadc78fab62
|
|
7
|
+
data.tar.gz: 449a1ca2f2d40441fcb871ce67b5fe7b45f61a7a08b14d774dcb5a6dd636ca5994fde435fa73ed0bb67abedf3f90cebcea970a8126e5a29ed888bfc16c926cd9
|
data/lib/iscc_lib/version.rb
CHANGED
data/lib/iscc_lib.rb
CHANGED
|
@@ -63,6 +63,9 @@ module IsccLib
|
|
|
63
63
|
# Result from gen_sum_code_v0.
|
|
64
64
|
class SumCodeResult < Result; end
|
|
65
65
|
|
|
66
|
+
# Result from gen_iscc_id_v1.
|
|
67
|
+
class IdCodeResult < Result; end
|
|
68
|
+
|
|
66
69
|
# Generate a Meta-Code from name and optional metadata.
|
|
67
70
|
#
|
|
68
71
|
# @param name [String] content name (required, non-empty)
|
|
@@ -157,6 +160,21 @@ module IsccLib
|
|
|
157
160
|
SumCodeResult[_gen_sum_code_v0(path, bits, wide, add_units)]
|
|
158
161
|
end
|
|
159
162
|
|
|
163
|
+
# Generate an ISCC-IDv1 from a timestamp and a HUB-ID (experimental).
|
|
164
|
+
#
|
|
165
|
+
# Packs a 52-bit microsecond UTC timestamp and a 12-bit HUB-ID into an
|
|
166
|
+
# ISCC-ID unit with realm as SubType and Version V1. There is no dedicated
|
|
167
|
+
# decoder — use iscc_decode to decode.
|
|
168
|
+
#
|
|
169
|
+
# @param timestamp [Integer] microsecond UTC timestamp (0 <= timestamp < 2^52)
|
|
170
|
+
# @param hub_id [Integer] HUB-ID (0 <= hub_id < 4096)
|
|
171
|
+
# @param realm [Integer] realm (0 = testnet, 1 = mainnet)
|
|
172
|
+
# @return [IdCodeResult] hash with iscc
|
|
173
|
+
# @raise [RuntimeError] if any parameter is negative or out of range
|
|
174
|
+
def self.gen_iscc_id_v1(timestamp, hub_id, realm)
|
|
175
|
+
IdCodeResult[_gen_iscc_id_v1(timestamp, hub_id, realm)]
|
|
176
|
+
end
|
|
177
|
+
|
|
160
178
|
# Streaming Data-Code generator (reopens native class).
|
|
161
179
|
#
|
|
162
180
|
# Incrementally processes data with content-defined chunking and MinHash
|
data/src/lib.rs
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
//! under the `IsccLib` module. The pure Ruby wrapper in `lib/iscc_lib.rb`
|
|
5
5
|
//! provides idiomatic result classes and keyword arguments.
|
|
6
6
|
//!
|
|
7
|
-
//! Symbols (
|
|
7
|
+
//! Symbols (33 of 33):
|
|
8
8
|
//! - `gen_meta_code_v0`, `gen_text_code_v0`, `gen_image_code_v0`, `gen_audio_code_v0`
|
|
9
9
|
//! - `gen_video_code_v0`, `gen_mixed_code_v0`, `gen_data_code_v0`
|
|
10
|
-
//! - `gen_instance_code_v0`, `gen_iscc_code_v0`, `gen_sum_code_v0`
|
|
10
|
+
//! - `gen_instance_code_v0`, `gen_iscc_code_v0`, `gen_sum_code_v0`, `gen_iscc_id_v1`
|
|
11
11
|
//! - `text_clean`, `text_remove_newlines`, `text_trim`, `text_collapse`
|
|
12
12
|
//! - `encode_base64`, `iscc_decompose`, `encode_component`, `iscc_decode`
|
|
13
13
|
//! - `json_to_data_url`, `conformance_selftest`
|
|
@@ -17,12 +17,15 @@
|
|
|
17
17
|
//! - Constants: META_TRIM_NAME, META_TRIM_DESCRIPTION, META_TRIM_META,
|
|
18
18
|
//! IO_READ_SIZE, TEXT_NGRAM_SIZE
|
|
19
19
|
|
|
20
|
-
use magnus::{
|
|
20
|
+
use magnus::{
|
|
21
|
+
Error, Integer, RArray, RHash, RString, Ruby, TryConvert, function, method, prelude::*,
|
|
22
|
+
};
|
|
21
23
|
use std::cell::RefCell;
|
|
22
24
|
|
|
23
25
|
/// Map an `IsccError` to a Magnus `RuntimeError`.
|
|
24
26
|
fn to_magnus_err(e: iscc_lib::IsccError) -> Error {
|
|
25
|
-
|
|
27
|
+
let ruby = Ruby::get().expect("called from Ruby");
|
|
28
|
+
Error::new(ruby.exception_runtime_error(), e.to_string())
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
/// Generate a Meta-Code from name and optional metadata.
|
|
@@ -187,6 +190,55 @@ fn gen_sum_code_v0(path: String, bits: u32, wide: bool, add_units: bool) -> Resu
|
|
|
187
190
|
Ok(hash)
|
|
188
191
|
}
|
|
189
192
|
|
|
193
|
+
/// Validate an ISCC-IDv1 parameter before narrowing to a fixed-width integer.
|
|
194
|
+
///
|
|
195
|
+
/// Ruby Integers are arbitrary precision, so each parameter is taken as a
|
|
196
|
+
/// `magnus::Integer` and range-checked here — converting in the signature
|
|
197
|
+
/// would raise `RangeError` during argument marshalling for values beyond
|
|
198
|
+
/// `i64`, breaking the normative `timestamp -> hub_id -> realm` first-failure
|
|
199
|
+
/// order. Rejects negative and out-of-range (`>= max_exclusive`) values with
|
|
200
|
+
/// a `RuntimeError` so an invalid Ruby Integer raises instead of being
|
|
201
|
+
/// silently narrowed to a wrong ID.
|
|
202
|
+
fn checked(value: Integer, max_exclusive: u64, name: &str) -> Result<u64, Error> {
|
|
203
|
+
let ruby = Ruby::get().expect("called from Ruby");
|
|
204
|
+
let v = value.to_u64().map_err(|_| {
|
|
205
|
+
Error::new(
|
|
206
|
+
ruby.exception_runtime_error(),
|
|
207
|
+
format!("Invalid {name}: out of range"),
|
|
208
|
+
)
|
|
209
|
+
})?;
|
|
210
|
+
if v >= max_exclusive {
|
|
211
|
+
return Err(Error::new(
|
|
212
|
+
ruby.exception_runtime_error(),
|
|
213
|
+
format!("Invalid {name}: {v}"),
|
|
214
|
+
));
|
|
215
|
+
}
|
|
216
|
+
Ok(v)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/// Generate an ISCC-IDv1 from a timestamp and a HUB-ID (experimental).
|
|
220
|
+
///
|
|
221
|
+
/// Packs a 52-bit microsecond UTC `timestamp` into the high bits and a 12-bit
|
|
222
|
+
/// `hub_id` (0-4095) into the low bits, encoding the result as an ISCC-ID unit
|
|
223
|
+
/// with `realm` (0 = testnet, 1 = mainnet) as SubType and Version `V1`. The
|
|
224
|
+
/// three semantic thresholds are validated in `timestamp` → `hub_id` → `realm`
|
|
225
|
+
/// order before narrowing, so the first failing check wins.
|
|
226
|
+
///
|
|
227
|
+
/// Returns a Ruby Hash with key: `iscc`. Raises `RuntimeError` if any parameter
|
|
228
|
+
/// is negative, or if `timestamp >= 2^52`, `hub_id >= 2^12`, or `realm` is not
|
|
229
|
+
/// `0` or `1` — for any Integer magnitude, including values beyond `i64`.
|
|
230
|
+
fn gen_iscc_id_v1(timestamp: Integer, hub_id: Integer, realm: Integer) -> Result<RHash, Error> {
|
|
231
|
+
let timestamp = checked(timestamp, 1u64 << 52, "timestamp")?;
|
|
232
|
+
let hub_id = checked(hub_id, 4096, "hub_id")?;
|
|
233
|
+
let realm = checked(realm, 2, "realm")?;
|
|
234
|
+
let r =
|
|
235
|
+
iscc_lib::gen_iscc_id_v1(timestamp, hub_id as u16, realm as u8).map_err(to_magnus_err)?;
|
|
236
|
+
let ruby = Ruby::get().expect("called from Ruby");
|
|
237
|
+
let hash = ruby.hash_new();
|
|
238
|
+
hash.aset("iscc", r.iscc)?;
|
|
239
|
+
Ok(hash)
|
|
240
|
+
}
|
|
241
|
+
|
|
190
242
|
/// Clean and normalize text for display.
|
|
191
243
|
///
|
|
192
244
|
/// Applies NFKC normalization, removes control characters (except newlines),
|
|
@@ -265,7 +317,7 @@ fn iscc_decode(iscc: String) -> Result<RArray, Error> {
|
|
|
265
317
|
arr.push(st)?;
|
|
266
318
|
arr.push(vs)?;
|
|
267
319
|
arr.push(li)?;
|
|
268
|
-
arr.push(
|
|
320
|
+
arr.push(ruby.str_from_slice(&digest))?;
|
|
269
321
|
Ok(arr)
|
|
270
322
|
}
|
|
271
323
|
|
|
@@ -307,7 +359,8 @@ fn alg_simhash(hash_digests: RArray) -> Result<RString, Error> {
|
|
|
307
359
|
})
|
|
308
360
|
.collect::<Result<Vec<_>, Error>>()?;
|
|
309
361
|
let result = iscc_lib::alg_simhash(&digests).map_err(to_magnus_err)?;
|
|
310
|
-
|
|
362
|
+
let ruby = Ruby::get().expect("called from Ruby");
|
|
363
|
+
Ok(ruby.str_from_slice(&result))
|
|
311
364
|
}
|
|
312
365
|
|
|
313
366
|
/// Compute a 256-bit MinHash digest from 32-bit integer features.
|
|
@@ -315,7 +368,8 @@ fn alg_simhash(hash_digests: RArray) -> Result<RString, Error> {
|
|
|
315
368
|
/// Returns a 32-byte binary String.
|
|
316
369
|
fn alg_minhash_256(features: Vec<u32>) -> RString {
|
|
317
370
|
let result = iscc_lib::alg_minhash_256(&features);
|
|
318
|
-
|
|
371
|
+
let ruby = Ruby::get().expect("called from Ruby");
|
|
372
|
+
ruby.str_from_slice(&result)
|
|
319
373
|
}
|
|
320
374
|
|
|
321
375
|
/// Split data into content-defined chunks using gear rolling hash.
|
|
@@ -330,7 +384,7 @@ fn alg_cdc_chunks(data: RString, utf32: bool, avg_chunk_size: u32) -> Result<RAr
|
|
|
330
384
|
let ruby = Ruby::get().expect("called from Ruby");
|
|
331
385
|
let arr = ruby.ary_new_capa(chunks.len());
|
|
332
386
|
for chunk in chunks {
|
|
333
|
-
arr.push(
|
|
387
|
+
arr.push(ruby.str_from_slice(chunk))?;
|
|
334
388
|
}
|
|
335
389
|
Ok(arr)
|
|
336
390
|
}
|
|
@@ -348,7 +402,8 @@ fn soft_hash_video_v0(frame_sigs: RArray, bits: u32) -> Result<RString, Error> {
|
|
|
348
402
|
})
|
|
349
403
|
.collect::<Result<Vec<_>, Error>>()?;
|
|
350
404
|
let result = iscc_lib::soft_hash_video_v0(&frames, bits).map_err(to_magnus_err)?;
|
|
351
|
-
|
|
405
|
+
let ruby = Ruby::get().expect("called from Ruby");
|
|
406
|
+
Ok(ruby.str_from_slice(&result))
|
|
352
407
|
}
|
|
353
408
|
|
|
354
409
|
/// Streaming Data-Code generator for Ruby.
|
|
@@ -372,10 +427,11 @@ impl RbDataHasher {
|
|
|
372
427
|
///
|
|
373
428
|
/// Raises `RuntimeError` if called after `finalize`.
|
|
374
429
|
fn update(&self, data: RString) -> Result<(), Error> {
|
|
430
|
+
let ruby = Ruby::get().expect("called from Ruby");
|
|
375
431
|
let mut inner = self.inner.borrow_mut();
|
|
376
432
|
let hasher = inner.as_mut().ok_or_else(|| {
|
|
377
433
|
Error::new(
|
|
378
|
-
|
|
434
|
+
ruby.exception_runtime_error(),
|
|
379
435
|
"DataHasher already finalized",
|
|
380
436
|
)
|
|
381
437
|
})?;
|
|
@@ -391,14 +447,14 @@ impl RbDataHasher {
|
|
|
391
447
|
/// Returns an `RHash` with key `"iscc"`. Raises `RuntimeError` if
|
|
392
448
|
/// called more than once.
|
|
393
449
|
fn finalize(&self, bits: u32) -> Result<RHash, Error> {
|
|
450
|
+
let ruby = Ruby::get().expect("called from Ruby");
|
|
394
451
|
let hasher = self.inner.borrow_mut().take().ok_or_else(|| {
|
|
395
452
|
Error::new(
|
|
396
|
-
|
|
453
|
+
ruby.exception_runtime_error(),
|
|
397
454
|
"DataHasher already finalized",
|
|
398
455
|
)
|
|
399
456
|
})?;
|
|
400
457
|
let r = hasher.finalize(bits).map_err(to_magnus_err)?;
|
|
401
|
-
let ruby = Ruby::get().expect("called from Ruby");
|
|
402
458
|
let hash = ruby.hash_new();
|
|
403
459
|
hash.aset("iscc", r.iscc)?;
|
|
404
460
|
Ok(hash)
|
|
@@ -426,10 +482,11 @@ impl RbInstanceHasher {
|
|
|
426
482
|
///
|
|
427
483
|
/// Raises `RuntimeError` if called after `finalize`.
|
|
428
484
|
fn update(&self, data: RString) -> Result<(), Error> {
|
|
485
|
+
let ruby = Ruby::get().expect("called from Ruby");
|
|
429
486
|
let mut inner = self.inner.borrow_mut();
|
|
430
487
|
let hasher = inner.as_mut().ok_or_else(|| {
|
|
431
488
|
Error::new(
|
|
432
|
-
|
|
489
|
+
ruby.exception_runtime_error(),
|
|
433
490
|
"InstanceHasher already finalized",
|
|
434
491
|
)
|
|
435
492
|
})?;
|
|
@@ -445,14 +502,14 @@ impl RbInstanceHasher {
|
|
|
445
502
|
/// Returns an `RHash` with keys `"iscc"`, `"datahash"`, `"filesize"`.
|
|
446
503
|
/// Raises `RuntimeError` if called more than once.
|
|
447
504
|
fn finalize(&self, bits: u32) -> Result<RHash, Error> {
|
|
505
|
+
let ruby = Ruby::get().expect("called from Ruby");
|
|
448
506
|
let hasher = self.inner.borrow_mut().take().ok_or_else(|| {
|
|
449
507
|
Error::new(
|
|
450
|
-
|
|
508
|
+
ruby.exception_runtime_error(),
|
|
451
509
|
"InstanceHasher already finalized",
|
|
452
510
|
)
|
|
453
511
|
})?;
|
|
454
512
|
let r = hasher.finalize(bits).map_err(to_magnus_err)?;
|
|
455
|
-
let ruby = Ruby::get().expect("called from Ruby");
|
|
456
513
|
let hash = ruby.hash_new();
|
|
457
514
|
hash.aset("iscc", r.iscc)?;
|
|
458
515
|
hash.aset("datahash", r.datahash)?;
|
|
@@ -477,6 +534,7 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
477
534
|
module.define_module_function("_gen_instance_code_v0", function!(gen_instance_code_v0, 2))?;
|
|
478
535
|
module.define_module_function("_gen_iscc_code_v0", function!(gen_iscc_code_v0, 2))?;
|
|
479
536
|
module.define_module_function("_gen_sum_code_v0", function!(gen_sum_code_v0, 4))?;
|
|
537
|
+
module.define_module_function("_gen_iscc_id_v1", function!(gen_iscc_id_v1, 3))?;
|
|
480
538
|
|
|
481
539
|
// Text utility functions
|
|
482
540
|
module.define_module_function("text_clean", function!(text_clean, 1))?;
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: iscc-lib
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Titusz Pan
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-07-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: High-performance Ruby bindings for ISO 24138:2024 (ISCC). Native Rust
|
|
14
14
|
extension via Magnus for content identification and matching.
|