b4n1-boost 0.4.2 → 0.4.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7781402242b38972d803ea940c41f33d7eb2c5ddd90c59e1feae132231af6b94
4
- data.tar.gz: a993c57fdd0facfb21bea698327471063fc0da58cc3889b3fb305aeb53abd1ea
3
+ metadata.gz: b2f63ef0c8953fa639cb8ca11d1a9892c75dabcabd90519e13e0858934ae5448
4
+ data.tar.gz: b16352e42cbd457dc10a4dedcba692c3b08f3d997fe16bfa472f554ca1946389
5
5
  SHA512:
6
- metadata.gz: f9e8bba9eee9abc810d193b8143da2efe57031e0b3e24238057a3c665e59c7030828d23f1f4163d6c9e2b709a0ec401ac7462157c72d1955afffd1905b501dce
7
- data.tar.gz: f0fa7a531b88493ee7fd89b36de6339edf19463b67c9e53a5c4f5426f1f5a87148ad41c68c2978b0c089806d6c41a5fac5a2c739b39c3e5d7706843c72830262
6
+ metadata.gz: 7e43fbdd59218e909db7ba8e184f682dad1955bb71c4f31d62e3c2690e58f87fad743bb4a0b7d547d8f05a492446888e9c209a25831f6e738d4ca9e9a7f36297
7
+ data.tar.gz: 48d53e75e0397aed2472d07382b60d6bab26c61db8acd6d91025ccb4adf091ecf09f5d14cbb12cdd4710bdc6e7f4a8ce110bd85c48118dd1bdcca73e900eb442
data/core.rb ADDED
@@ -0,0 +1,143 @@
1
+ # B4N1-Boost Ruby SDK — core primitives: response cache, bloom filter,
2
+ # circuit breaker. Business Source License 1.1 (BUSL-1.1).
3
+ # Thin FFI wrapper over the proprietary B4N1 native core. ALL logic (LRU
4
+ # eviction, TTL, bloom hashing, circuit thresholds) lives in the native core
5
+ # and is shared with Python, Node.js, .NET and Java through the same C-ABI —
6
+ # a fix in the core fixes every SDK at once.
7
+ # The native binary is licensed separately under the B4N1 EULA (EULA.txt).
8
+ # Copyright (c) 2026 B4N1. All rights reserved.
9
+ require_relative 'boost'
10
+
11
+ module B4N1
12
+ module Boost
13
+ extern 'uint64_t b4n1_cache_new(int)'
14
+ extern 'int b4n1_cache_get(uint64_t, char*, void**, size_t*, uint16_t*)'
15
+ extern 'int b4n1_cache_set(uint64_t, char*, char*, size_t, uint16_t, uint64_t)'
16
+ extern 'int b4n1_cache_invalidate(uint64_t, char*)'
17
+ extern 'void b4n1_cache_clear(uint64_t)'
18
+ extern 'void b4n1_cache_free(uint64_t)'
19
+ extern 'uint64_t b4n1_bloom_new(uint64_t, uint64_t)'
20
+ extern 'int b4n1_bloom_check_and_set(uint64_t, char*)'
21
+ extern 'void b4n1_bloom_free(uint64_t)'
22
+ extern 'uint64_t b4n1_circuit_new(int, int, uint64_t)'
23
+ extern 'int b4n1_circuit_allows(uint64_t)'
24
+ extern 'void b4n1_circuit_record_success(uint64_t)'
25
+ extern 'void b4n1_circuit_record_failure(uint64_t)'
26
+ extern 'void b4n1_circuit_free(uint64_t)'
27
+
28
+ # Response cache (LRU + TTL) backed by the native core.
29
+ #
30
+ # cache = B4N1::Boost::ResponseCache.new(1024)
31
+ # cache.set('user:1', body, 200, 60) # ttl_secs 0 = never expires
32
+ # hit = cache.get('user:1') # => { body:, status: } or nil
33
+ # hit[:body] # binary-safe String
34
+ class ResponseCache
35
+ def initialize(capacity = 1024)
36
+ @handle = Boost.b4n1_cache_new(capacity)
37
+ raise ArgumentError, 'Response cache capacity out of bounds (max 1_000_000)' if @handle.zero?
38
+
39
+ @handle
40
+ end
41
+
42
+ # Returns { body:, status: } on a hit, nil on a miss.
43
+ def get(key)
44
+ out_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
45
+ out_len = Fiddle::Pointer.malloc(Fiddle::SIZEOF_SIZE_T)
46
+ out_status = Fiddle::Pointer.malloc(2)
47
+ return nil unless Boost.b4n1_cache_get(@handle, key, out_ptr, out_len, out_status) == 1
48
+
49
+ addr = out_ptr[0, Fiddle::SIZEOF_VOIDP].unpack1('J')
50
+ len = out_len[0, Fiddle::SIZEOF_SIZE_T].unpack1('J')
51
+ # Copy before releasing: the core owns that buffer.
52
+ body = Fiddle::Pointer.new(addr).to_str(len)
53
+ Boost.b4n1_boost_free_buffer(Fiddle::Pointer.new(addr), len)
54
+ { body: body, status: out_status[0, 2].unpack1('S') }
55
+ end
56
+
57
+ # ttl_secs == 0 means never expires (core semantics, matches Node).
58
+ def set(key, body, status = 200, ttl_secs = 300)
59
+ bytes = body.to_s.b
60
+ rc = Boost.b4n1_cache_set(@handle, key, bytes.empty? ? nil : bytes, bytes.bytesize, status, ttl_secs)
61
+ raise 'Response cache set failed (stale handle?)' unless rc.zero?
62
+ end
63
+
64
+ # Returns true when the key existed and was removed.
65
+ def invalidate(key)
66
+ Boost.b4n1_cache_invalidate(@handle, key) == 1
67
+ end
68
+
69
+ def clear
70
+ Boost.b4n1_cache_clear(@handle)
71
+ end
72
+
73
+ def close
74
+ return if @handle.nil? || @handle.zero?
75
+
76
+ Boost.b4n1_cache_free(@handle)
77
+ @handle = 0
78
+ end
79
+ end
80
+
81
+ # Bloom filter for rate limiting / dedup.
82
+ #
83
+ # seen = B4N1::Boost::BloomFilter.new(100_000, 0.01)
84
+ # seen.check_and_set('req-1') # => false (first time, now recorded)
85
+ # seen.check_and_set('req-1') # => true (duplicate)
86
+ class BloomFilter
87
+ def initialize(expected_items = 10_000, fp_rate = 0.01)
88
+ # The core reads the double from its u64 bit pattern.
89
+ bits = [fp_rate.to_f].pack('d').unpack1('Q')
90
+ @handle = Boost.b4n1_bloom_new(expected_items, bits)
91
+ raise ArgumentError, 'Bloom expected items out of bounds (max 10_000_000)' if @handle.zero?
92
+ end
93
+
94
+ # Returns true when the item was already present (duplicate).
95
+ def check_and_set(item)
96
+ Boost.b4n1_bloom_check_and_set(@handle, item) == 1
97
+ end
98
+
99
+ def close
100
+ return if @handle.nil? || @handle.zero?
101
+
102
+ Boost.b4n1_bloom_free(@handle)
103
+ @handle = 0
104
+ end
105
+ end
106
+
107
+ # Circuit breaker: allows? is false while the circuit is open.
108
+ #
109
+ # breaker = B4N1::Boost::CircuitBreaker.new(5, 2, 30)
110
+ # fallback unless breaker.allows?
111
+ # breaker.record_failure # or record_success after a good call
112
+ class CircuitBreaker
113
+ def initialize(threshold = 5, success_threshold = 1, timeout_secs = 30)
114
+ @handle = Boost.b4n1_circuit_new(threshold, success_threshold, timeout_secs)
115
+ raise 'Circuit breaker handle invalid' if @handle.zero?
116
+ end
117
+
118
+ # Returns false when calls must NOT go through (circuit open, or the
119
+ # breaker was closed — a closed breaker must fail CLOSED, never
120
+ # resurrect through the C-ABI's 0-handle legacy path).
121
+ def allows?
122
+ return false if @handle.nil? || @handle.zero?
123
+
124
+ Boost.b4n1_circuit_allows(@handle) == 1
125
+ end
126
+
127
+ def record_success
128
+ Boost.b4n1_circuit_record_success(@handle)
129
+ end
130
+
131
+ def record_failure
132
+ Boost.b4n1_circuit_record_failure(@handle)
133
+ end
134
+
135
+ def close
136
+ return if @handle.nil? || @handle.zero?
137
+
138
+ Boost.b4n1_circuit_free(@handle)
139
+ @handle = 0
140
+ end
141
+ end
142
+ end
143
+ end
Binary file
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: b4n1-boost
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - B4N1 Team
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-23 00:00:00.000000000 Z
11
+ date: 2026-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -36,6 +36,7 @@ files:
36
36
  - LICENSE
37
37
  - README.md
38
38
  - boost.rb
39
+ - core.rb
39
40
  - lib/native/linux-arm64/libb4n1_boost.so
40
41
  - lib/native/linux-x64/libb4n1_boost.so
41
42
  - lib/native/macos-arm64/libb4n1_boost.dylib
@@ -46,7 +47,7 @@ homepage: https://github.com/B4N1-com/private-repos
46
47
  licenses:
47
48
  - BUSL-1.1
48
49
  metadata: {}
49
- post_install_message:
50
+ post_install_message:
50
51
  rdoc_options: []
51
52
  require_paths:
52
53
  - "."
@@ -61,8 +62,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
61
62
  - !ruby/object:Gem::Version
62
63
  version: '0'
63
64
  requirements: []
64
- rubygems_version: 3.3.5
65
- signing_key:
65
+ rubygems_version: 3.4.20
66
+ signing_key:
66
67
  specification_version: 4
67
68
  summary: High-performance native Rust boost layer for Ruby & Rack/Rails.
68
69
  test_files: []