diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 120a1c0..41c7eee 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -22,14 +22,14 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['2.6', '2.7', '3.0', '3.3'] + ruby-version: ['3.2', '3.3', '3.4', '4.0'] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} - bundler-cache: true # runs 'bundle install' and caches installed gems automatically + bundler-cache: true - name: Run tests run: bundle exec rake diff --git a/CHANGELOG.md b/CHANGELOG.md index 41d9ff4..9e2763a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ -### master +### 0.7.0 (August 11, 2025) + * Make it compatible with gcc 15 (by [@lemmuh](https://github.com/lemmuh)) + +### 0.6.0 (December 19, 2024) * Update libxxhash to 0.8.1 + * Drop old Rubies. Support only MRI 3.1+ + * Modernize Ruby C API ussage, fix related warnings. (by [@casperisfine](https://github.com/casperisfine) and [@byroot](https://github.com/byroot)) ### 0.5.0 (July 28, 2022) * Mark extension as Ractor-safe (by [@kreynolds](https://github.com/kreynolds)) diff --git a/README.md b/README.md index b61b759..1f6b0c8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## xxHash [![Build Status](https://travis-ci.org/nashby/xxhash.png?branch=master)](https://travis-ci.org/nashby/xxhash) +## xxHash [![Ruby](https://github.com/nashby/xxhash/actions/workflows/ruby.yml/badge.svg)](https://github.com/nashby/xxhash/actions/workflows/ruby.yml) Ruby wrapper for [xxHash](https://github.com/Cyan4973/xxHash) @@ -37,14 +37,13 @@ XXH64 is also supported: you can use `xxh64`, `xxh64_stream`, `.xxh64_file`. ### Supported Ruby versions -- MRI 2.3+ -- rbx-19mode +- MRI 3.1+ Note: It doesn't work on JRuby as it uses C extension. ### Versioning -Version 0.5.0 is equal to [0.6.2](https://github.com/Cyan4973/xxHash/tree/v0.6.2) +Version 0.7.0 is equal to [0.8.1](https://github.com/Cyan4973/xxHash/tree/v0.8.1) ## Contributing @@ -56,5 +55,5 @@ Version 0.5.0 is equal to [0.6.2](https://github.com/Cyan4973/xxHash/tree/v0.6.2 ### Copyright -Copyright (c) 2022 Vasiliy Ermolovich. See LICENSE.txt for +Copyright (c) 2024 Vasiliy Ermolovich. See LICENSE.txt for further details. diff --git a/ext/xxhash/xxhash.c b/ext/xxhash/xxhash.c index 82e5ac8..9861f83 100644 --- a/ext/xxhash/xxhash.c +++ b/ext/xxhash/xxhash.c @@ -67,13 +67,30 @@ VALUE xxhash_xxh32(VALUE mod, VALUE input, VALUE seed) return ULL2NUM(XXH32(StringValuePtr(input), (size_t)RSTRING_LEN(input), (unsigned int)NUM2ULL(seed))); } -void xxhash32_streaming_hash_free(xxhash_xxh32_t* storage) +static void xxhash32_streaming_hash_free(void *ptr) { + xxhash_xxh32_t* storage = (xxhash_xxh32_t*)ptr; // Digest frees the memory. XXH32_freeState(storage->state); xfree(storage); } +static size_t xxhash32_streaming_hash_memsize(const void *ptr) +{ + // Ideally we'd include sizeof(XXH32_state_t) too, but the type is opaque. + return sizeof(xxhash_xxh32_t); +} + +static const rb_data_type_t xxhash_xxh32_type = { + .wrap_struct_name = "xxhash/xxhash32_streaming_hash", + .function = { + .dmark = NULL, + .dfree = xxhash32_streaming_hash_free, + .dsize = xxhash32_streaming_hash_memsize, + }, + .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY +}; + VALUE xxhash32_streaming_hash_new(VALUE klass, VALUE seed) { XXH_errorcode code; @@ -86,14 +103,14 @@ VALUE xxhash32_streaming_hash_new(VALUE klass, VALUE seed) rb_raise(rb_eRuntimeError, "Error during reset."); return Qnil; } - return Data_Wrap_Struct(klass, 0, xxhash32_streaming_hash_free, storage); + return TypedData_Wrap_Struct(klass, &xxhash_xxh32_type, storage); } VALUE xxhash32_streaming_hash_reset(VALUE self) { XXH_errorcode code; xxhash_xxh32_t* storage; - Data_Get_Struct(self, xxhash_xxh32_t, storage); + TypedData_Get_Struct(self, xxhash_xxh32_t, &xxhash_xxh32_type, storage); code = XXH32_reset(storage->state, storage->seed); if(code != XXH_OK) { @@ -107,7 +124,7 @@ VALUE xxhash32_streaming_hash_update(VALUE self, VALUE data) { XXH_errorcode code; xxhash_xxh32_t* storage; - Data_Get_Struct(self, xxhash_xxh32_t, storage); + TypedData_Get_Struct(self, xxhash_xxh32_t, &xxhash_xxh32_type, storage); code = XXH32_update(storage->state, StringValuePtr(data), (size_t)RSTRING_LEN(data)); if(code != XXH_OK) { @@ -119,7 +136,7 @@ VALUE xxhash32_streaming_hash_update(VALUE self, VALUE data) VALUE xxhash32_streaming_hash_digest(VALUE self) { xxhash_xxh32_t* storage; - Data_Get_Struct(self, xxhash_xxh32_t, storage); + TypedData_Get_Struct(self, xxhash_xxh32_t, &xxhash_xxh32_type, storage); // Do not free memory now. return ULL2NUM(XXH32_digest(storage->state)); @@ -130,13 +147,30 @@ VALUE xxhash_xxh64(VALUE mod, VALUE input, VALUE seed) return ULL2NUM(XXH64(StringValuePtr(input), (size_t)RSTRING_LEN(input), (unsigned int)NUM2ULL(seed))); } -void xxhash64_streaming_hash_free(xxhash_xxh64_t* storage) +static void xxhash64_streaming_hash_free(void *ptr) { + xxhash_xxh64_t* storage = (xxhash_xxh64_t*)ptr; // Digest frees the memory. XXH64_freeState(storage->state); xfree(storage); } +static size_t xxhash64_streaming_hash_memsize(const void *ptr) +{ + // Ideally we'd include sizeof(XXH64_state_t) too, but the type is opaque. + return sizeof(xxhash_xxh64_t); +} + +static const rb_data_type_t xxhash_xxh64_type = { + .wrap_struct_name = "xxhash/xxhash64_streaming_hash", + .function = { + .dmark = NULL, + .dfree = xxhash64_streaming_hash_free, + .dsize = xxhash64_streaming_hash_memsize, + }, + .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY +}; + VALUE xxhash64_streaming_hash_new(VALUE klass, VALUE seed) { XXH_errorcode code; @@ -150,14 +184,14 @@ VALUE xxhash64_streaming_hash_new(VALUE klass, VALUE seed) rb_raise(rb_eRuntimeError, "Error during reset."); return Qnil; } - return Data_Wrap_Struct(klass, 0, xxhash64_streaming_hash_free, storage); + return TypedData_Wrap_Struct(klass, &xxhash_xxh64_type, storage); } VALUE xxhash64_streaming_hash_reset(VALUE self) { XXH_errorcode code; xxhash_xxh64_t* storage; - Data_Get_Struct(self, xxhash_xxh64_t, storage); + TypedData_Get_Struct(self, xxhash_xxh64_t, &xxhash_xxh64_type, storage); code = XXH64_reset(storage->state, storage->seed); if(code != XXH_OK) { @@ -170,7 +204,7 @@ VALUE xxhash64_streaming_hash_update(VALUE self, VALUE data) { XXH_errorcode code; xxhash_xxh64_t* storage; - Data_Get_Struct(self, xxhash_xxh64_t, storage); + TypedData_Get_Struct(self, xxhash_xxh64_t, &xxhash_xxh64_type, storage); code = XXH64_update(storage->state, StringValuePtr(data), (size_t)RSTRING_LEN(data)); if(code != XXH_OK) { @@ -182,7 +216,7 @@ VALUE xxhash64_streaming_hash_update(VALUE self, VALUE data) VALUE xxhash64_streaming_hash_digest(VALUE self) { xxhash_xxh64_t* storage; - Data_Get_Struct(self, xxhash_xxh64_t, storage); + TypedData_Get_Struct(self, xxhash_xxh64_t, &xxhash_xxh64_type, storage); // Do not free memory now. return ULL2NUM(XXH64_digest(storage->state)); @@ -203,22 +237,24 @@ void Init_xxhash(void) mXXhash = rb_define_module("XXhash"); mInternal = rb_define_module_under(mXXhash, "XXhashInternal"); - rb_define_singleton_method(mInternal, "xxh32", (ruby_method*) &xxhash_xxh32, 2); - rb_define_singleton_method(mInternal, "xxh32_file", (ruby_method*) &xxhash_xxh32_file, 2); - rb_define_singleton_method(mInternal, "xxh64", (ruby_method*) &xxhash_xxh64, 2); - rb_define_singleton_method(mInternal, "xxh64_file", (ruby_method*) &xxhash_xxh64_file, 2); + rb_define_singleton_method(mInternal, "xxh32", xxhash_xxh32, 2); + rb_define_singleton_method(mInternal, "xxh32_file", xxhash_xxh32_file, 2); + rb_define_singleton_method(mInternal, "xxh64", xxhash_xxh64, 2); + rb_define_singleton_method(mInternal, "xxh64_file", xxhash_xxh64_file, 2); cStreamingHash = rb_define_class_under(mInternal, "StreamingHash32", rb_cObject); + rb_undef_alloc_func(cStreamingHash); - rb_define_singleton_method(cStreamingHash, "new", (ruby_method*) &xxhash32_streaming_hash_new, 1); - rb_define_method(cStreamingHash, "update", (ruby_method*) &xxhash32_streaming_hash_update, 1); - rb_define_method(cStreamingHash, "digest", (ruby_method*) &xxhash32_streaming_hash_digest, 0); - rb_define_method(cStreamingHash, "reset", (ruby_method*) &xxhash32_streaming_hash_reset, 0); + rb_define_singleton_method(cStreamingHash, "new", xxhash32_streaming_hash_new, 1); + rb_define_method(cStreamingHash, "update", xxhash32_streaming_hash_update, 1); + rb_define_method(cStreamingHash, "digest", xxhash32_streaming_hash_digest, 0); + rb_define_method(cStreamingHash, "reset", xxhash32_streaming_hash_reset, 0); cStreamingHash64 = rb_define_class_under(mInternal, "StreamingHash64", rb_cObject); + rb_undef_alloc_func(cStreamingHash64); - rb_define_singleton_method(cStreamingHash64, "new", (ruby_method*) &xxhash64_streaming_hash_new, 1); - rb_define_method(cStreamingHash64, "update", (ruby_method*) &xxhash64_streaming_hash_update, 1); - rb_define_method(cStreamingHash64, "digest", (ruby_method*) &xxhash64_streaming_hash_digest, 0); - rb_define_method(cStreamingHash64, "reset", (ruby_method*) &xxhash64_streaming_hash_reset, 0); + rb_define_singleton_method(cStreamingHash64, "new", xxhash64_streaming_hash_new, 1); + rb_define_method(cStreamingHash64, "update", xxhash64_streaming_hash_update, 1); + rb_define_method(cStreamingHash64, "digest", xxhash64_streaming_hash_digest, 0); + rb_define_method(cStreamingHash64, "reset", xxhash64_streaming_hash_reset, 0); } diff --git a/ext/xxhash/xxhash.h b/ext/xxhash/xxhash.h index 8b2b626..5926960 100644 --- a/ext/xxhash/xxhash.h +++ b/ext/xxhash/xxhash.h @@ -19,14 +19,12 @@ typedef VALUE (ruby_method)(); VALUE xxhash_xxh32(VALUE mod, VALUE input, VALUE seed); VALUE xxhash_xxh32_file(VALUE mod, VALUE filename, VALUE seed); -void xxhash32_streaming_hash_free(xxhash_xxh32_t* state); VALUE xxhash32_streaming_hash_new(VALUE klass, VALUE seed); VALUE xxhash32_streaming_hash_update(VALUE self, VALUE data); VALUE xxhash32_streaming_hash_reset(VALUE self); VALUE xxhash32_streaming_hash_digest(VALUE self); VALUE xxhash_xxh64(VALUE mod, VALUE input, VALUE seed); VALUE xxhash_xxh64_file(VALUE mod, VALUE filename, VALUE seed); -void xxhash64_streaming_hash_free(xxhash_xxh64_t* state); VALUE xxhash64_streaming_hash_new(VALUE klass, VALUE seed); VALUE xxhash64_streaming_hash_update(VALUE self, VALUE data); VALUE xxhash64_streaming_hash_reset(VALUE self); diff --git a/lib/xxhash/version.rb b/lib/xxhash/version.rb index 553131e..b04f3dc 100644 --- a/lib/xxhash/version.rb +++ b/lib/xxhash/version.rb @@ -1,3 +1,3 @@ module XXhash - VERSION = "0.5.0" + VERSION = "0.7.0" end diff --git a/test/xxhash_test.rb b/test/xxhash_test.rb index 7778670..93e5d71 100644 --- a/test/xxhash_test.rb +++ b/test/xxhash_test.rb @@ -3,14 +3,26 @@ describe XXhash do it 'is marked as ractor-safe' do - skip("Ractorrs are not supported in this version of ruby(#{RUBY_VERSION})") unless defined?(Ractor) + skip("Ractors are not supported in this version of ruby(#{RUBY_VERSION})") unless defined?(Ractor) - ractor = Ractor.new do - Ractor.yield XXhash.xxh32(Ractor.receive) - end + if defined?(Ractor::Port) + ractor = Ractor.new do + msg, reply_port = Ractor.receive + reply_port << XXhash.xxh32(msg) + end + + reply = Ractor::Port.new + ractor << ["test", reply] - ractor.send('test') - assert_equal ractor.take, XXhash.xxh32('test') + assert_equal reply.receive, XXhash.xxh32("test") + else + ractor = Ractor.new do + Ractor.yield XXhash.xxh32(Ractor.receive) + end + + ractor.send('test') + assert_equal ractor.take, XXhash.xxh32('test') + end end it 'returns 32-bit hash' do