tigerbeetle 0.0.37-aarch64-linux

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.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +152 -0
  3. data/LICENSE +201 -0
  4. data/README.md +130 -0
  5. data/lib/tb_client/native/aarch64-linux-gnu.2.27/libtb_client.a +0 -0
  6. data/lib/tb_client/native/aarch64-linux-gnu.2.27/libtb_client.so +0 -0
  7. data/lib/tb_client/native/aarch64-linux-musl/libtb_client.a +0 -0
  8. data/lib/tb_client/native/aarch64-linux-musl/libtb_client.so +0 -0
  9. data/lib/tb_client/shared_lib.rb +66 -0
  10. data/lib/tb_client.rb +282 -0
  11. data/lib/tigerbeetle/account.rb +38 -0
  12. data/lib/tigerbeetle/account_balance.rb +23 -0
  13. data/lib/tigerbeetle/account_filter.rb +31 -0
  14. data/lib/tigerbeetle/atomic_counter.rb +14 -0
  15. data/lib/tigerbeetle/client.rb +214 -0
  16. data/lib/tigerbeetle/converters/account.rb +63 -0
  17. data/lib/tigerbeetle/converters/account_balance.rb +31 -0
  18. data/lib/tigerbeetle/converters/account_filter.rb +32 -0
  19. data/lib/tigerbeetle/converters/base.rb +35 -0
  20. data/lib/tigerbeetle/converters/create_accounts_result.rb +21 -0
  21. data/lib/tigerbeetle/converters/create_transfers_result.rb +21 -0
  22. data/lib/tigerbeetle/converters/query_filter.rb +33 -0
  23. data/lib/tigerbeetle/converters/time.rb +23 -0
  24. data/lib/tigerbeetle/converters/transfer.rb +64 -0
  25. data/lib/tigerbeetle/converters/uint_128.rb +24 -0
  26. data/lib/tigerbeetle/converters.rb +12 -0
  27. data/lib/tigerbeetle/error.rb +4 -0
  28. data/lib/tigerbeetle/id.rb +30 -0
  29. data/lib/tigerbeetle/platforms.rb +9 -0
  30. data/lib/tigerbeetle/query_filter.rb +31 -0
  31. data/lib/tigerbeetle/request.rb +7 -0
  32. data/lib/tigerbeetle/transfer.rb +40 -0
  33. data/lib/tigerbeetle/version.rb +4 -0
  34. data/lib/tigerbeetle.rb +13 -0
  35. data/tigerbeetle.gemspec +50 -0
  36. metadata +133 -0
data/lib/tb_client.rb ADDED
@@ -0,0 +1,282 @@
1
+ require 'ffi'
2
+ require 'tb_client/shared_lib'
3
+
4
+ module TBClient
5
+ extend FFI::Library
6
+
7
+ ffi_lib SharedLib.path
8
+
9
+ InitStatus = enum(FFI::Type::UINT8, [
10
+ :SUCCESS, 0,
11
+ :UNEXPECTED,
12
+ :OUT_OF_MEMORY,
13
+ :ADDRESS_INVALID,
14
+ :ADDRESS_LIMIT_EXCEEDED,
15
+ :SYSTEM_RESOURCES,
16
+ :NETWORK_SUBSYSTEM])
17
+
18
+ ClientStatus = enum(FFI::Type::UINT8, [
19
+ :OK, 0,
20
+ :INVALID])
21
+
22
+ PacketStatus = enum(FFI::Type::UINT8, [
23
+ :OK, 0,
24
+ :TOO_MUCH_DATA,
25
+ :CLIENT_EVICTED,
26
+ :CLIENT_RELEASE_TOO_LOW,
27
+ :CLIENT_RELEASE_TOO_HIGH,
28
+ :CLIENT_SHUTDOWN,
29
+ :INVALID_OPERATION,
30
+ :INVALID_DATA_SIZE])
31
+
32
+ RegisterLogCallbackStatus = enum(FFI::Type::UINT8, [
33
+ :SUCCESS, 0,
34
+ :ALREADY_REGISTERED,
35
+ :NOT_REGISTERED])
36
+
37
+ Operation = enum(FFI::Type::UINT8, [
38
+ :PULSE, 128,
39
+ :GET_EVENTS, 137,
40
+ :CREATE_ACCOUNTS, 138,
41
+ :CREATE_TRANSFERS, 139,
42
+ :LOOKUP_ACCOUNTS, 140,
43
+ :LOOKUP_TRANSFERS, 141,
44
+ :GET_ACCOUNT_TRANSFERS, 142,
45
+ :GET_ACCOUNT_BALANCES, 143,
46
+ :QUERY_ACCOUNTS, 144,
47
+ :QUERY_TRANSFERS, 145])
48
+
49
+ CreateAccountResult = enum(FFI::Type::UINT32, [
50
+ :OK, 0,
51
+ :LINKED_EVENT_FAILED, 1,
52
+ :LINKED_EVENT_CHAIN_OPEN, 2,
53
+ :IMPORTED_EVENT_EXPECTED, 22,
54
+ :IMPORTED_EVENT_NOT_EXPECTED, 23,
55
+ :TIMESTAMP_MUST_BE_ZERO, 3,
56
+ :IMPORTED_EVENT_TIMESTAMP_OUT_OF_RANGE, 24,
57
+ :IMPORTED_EVENT_TIMESTAMP_MUST_NOT_ADVANCE, 25,
58
+ :RESERVED_FIELD, 4,
59
+ :RESERVED_FLAG, 5,
60
+ :ID_MUST_NOT_BE_ZERO, 6,
61
+ :ID_MUST_NOT_BE_INT_MAX, 7,
62
+ :EXISTS_WITH_DIFFERENT_FLAGS, 15,
63
+ :EXISTS_WITH_DIFFERENT_USER_DATA_128, 16,
64
+ :EXISTS_WITH_DIFFERENT_USER_DATA_64, 17,
65
+ :EXISTS_WITH_DIFFERENT_USER_DATA_32, 18,
66
+ :EXISTS_WITH_DIFFERENT_LEDGER, 19,
67
+ :EXISTS_WITH_DIFFERENT_CODE, 20,
68
+ :EXISTS, 21,
69
+ :FLAGS_ARE_MUTUALLY_EXCLUSIVE, 8,
70
+ :DEBITS_PENDING_MUST_BE_ZERO, 9,
71
+ :DEBITS_POSTED_MUST_BE_ZERO, 10,
72
+ :CREDITS_PENDING_MUST_BE_ZERO, 11,
73
+ :CREDITS_POSTED_MUST_BE_ZERO, 12,
74
+ :LEDGER_MUST_NOT_BE_ZERO, 13,
75
+ :CODE_MUST_NOT_BE_ZERO, 14,
76
+ :IMPORTED_EVENT_TIMESTAMP_MUST_NOT_REGRESS, 26])
77
+
78
+ CreateTransferResult = enum(FFI::Type::UINT32, [
79
+ :OK, 0,
80
+ :LINKED_EVENT_FAILED, 1,
81
+ :LINKED_EVENT_CHAIN_OPEN, 2,
82
+ :IMPORTED_EVENT_EXPECTED, 56,
83
+ :IMPORTED_EVENT_NOT_EXPECTED, 57,
84
+ :TIMESTAMP_MUST_BE_ZERO, 3,
85
+ :IMPORTED_EVENT_TIMESTAMP_OUT_OF_RANGE, 58,
86
+ :IMPORTED_EVENT_TIMESTAMP_MUST_NOT_ADVANCE, 59,
87
+ :RESERVED_FLAG, 4,
88
+ :ID_MUST_NOT_BE_ZERO, 5,
89
+ :ID_MUST_NOT_BE_INT_MAX, 6,
90
+ :EXISTS_WITH_DIFFERENT_FLAGS, 36,
91
+ :EXISTS_WITH_DIFFERENT_PENDING_ID, 40,
92
+ :EXISTS_WITH_DIFFERENT_TIMEOUT, 44,
93
+ :EXISTS_WITH_DIFFERENT_DEBIT_ACCOUNT_ID, 37,
94
+ :EXISTS_WITH_DIFFERENT_CREDIT_ACCOUNT_ID, 38,
95
+ :EXISTS_WITH_DIFFERENT_AMOUNT, 39,
96
+ :EXISTS_WITH_DIFFERENT_USER_DATA_128, 41,
97
+ :EXISTS_WITH_DIFFERENT_USER_DATA_64, 42,
98
+ :EXISTS_WITH_DIFFERENT_USER_DATA_32, 43,
99
+ :EXISTS_WITH_DIFFERENT_LEDGER, 67,
100
+ :EXISTS_WITH_DIFFERENT_CODE, 45,
101
+ :EXISTS, 46,
102
+ :ID_ALREADY_FAILED, 68,
103
+ :FLAGS_ARE_MUTUALLY_EXCLUSIVE, 7,
104
+ :DEBIT_ACCOUNT_ID_MUST_NOT_BE_ZERO, 8,
105
+ :DEBIT_ACCOUNT_ID_MUST_NOT_BE_INT_MAX, 9,
106
+ :CREDIT_ACCOUNT_ID_MUST_NOT_BE_ZERO, 10,
107
+ :CREDIT_ACCOUNT_ID_MUST_NOT_BE_INT_MAX, 11,
108
+ :ACCOUNTS_MUST_BE_DIFFERENT, 12,
109
+ :PENDING_ID_MUST_BE_ZERO, 13,
110
+ :PENDING_ID_MUST_NOT_BE_ZERO, 14,
111
+ :PENDING_ID_MUST_NOT_BE_INT_MAX, 15,
112
+ :PENDING_ID_MUST_BE_DIFFERENT, 16,
113
+ :TIMEOUT_RESERVED_FOR_PENDING_TRANSFER, 17,
114
+ :CLOSING_TRANSFER_MUST_BE_PENDING, 64,
115
+ :LEDGER_MUST_NOT_BE_ZERO, 19,
116
+ :CODE_MUST_NOT_BE_ZERO, 20,
117
+ :DEBIT_ACCOUNT_NOT_FOUND, 21,
118
+ :CREDIT_ACCOUNT_NOT_FOUND, 22,
119
+ :ACCOUNTS_MUST_HAVE_THE_SAME_LEDGER, 23,
120
+ :TRANSFER_MUST_HAVE_THE_SAME_LEDGER_AS_ACCOUNTS, 24,
121
+ :PENDING_TRANSFER_NOT_FOUND, 25,
122
+ :PENDING_TRANSFER_NOT_PENDING, 26,
123
+ :PENDING_TRANSFER_HAS_DIFFERENT_DEBIT_ACCOUNT_ID, 27,
124
+ :PENDING_TRANSFER_HAS_DIFFERENT_CREDIT_ACCOUNT_ID, 28,
125
+ :PENDING_TRANSFER_HAS_DIFFERENT_LEDGER, 29,
126
+ :PENDING_TRANSFER_HAS_DIFFERENT_CODE, 30,
127
+ :EXCEEDS_PENDING_TRANSFER_AMOUNT, 31,
128
+ :PENDING_TRANSFER_HAS_DIFFERENT_AMOUNT, 32,
129
+ :PENDING_TRANSFER_ALREADY_POSTED, 33,
130
+ :PENDING_TRANSFER_ALREADY_VOIDED, 34,
131
+ :PENDING_TRANSFER_EXPIRED, 35,
132
+ :IMPORTED_EVENT_TIMESTAMP_MUST_NOT_REGRESS, 60,
133
+ :IMPORTED_EVENT_TIMESTAMP_MUST_POSTDATE_DEBIT_ACCOUNT, 61,
134
+ :IMPORTED_EVENT_TIMESTAMP_MUST_POSTDATE_CREDIT_ACCOUNT, 62,
135
+ :IMPORTED_EVENT_TIMEOUT_MUST_BE_ZERO, 63,
136
+ :DEBIT_ACCOUNT_ALREADY_CLOSED, 65,
137
+ :CREDIT_ACCOUNT_ALREADY_CLOSED, 66,
138
+ :OVERFLOWS_DEBITS_PENDING, 47,
139
+ :OVERFLOWS_CREDITS_PENDING, 48,
140
+ :OVERFLOWS_DEBITS_POSTED, 49,
141
+ :OVERFLOWS_CREDITS_POSTED, 50,
142
+ :OVERFLOWS_DEBITS, 51,
143
+ :OVERFLOWS_CREDITS, 52,
144
+ :OVERFLOWS_TIMEOUT, 53,
145
+ :EXCEEDS_CREDITS, 54,
146
+ :EXCEEDS_DEBITS, 55])
147
+
148
+ LogLevel = enum(FFI::Type::UINT8, [
149
+ :ERR, 0,
150
+ :WARN,
151
+ :INFO,
152
+ :DEBUG])
153
+
154
+ AccountFlags = bitmask(FFI::Type::UINT16, [
155
+ :LINKED,
156
+ :DEBITS_MUST_NOT_EXCEED_CREDITS,
157
+ :CREDITS_MUST_NOT_EXCEED_DEBITS,
158
+ :HISTORY,
159
+ :IMPORTED,
160
+ :CLOSED]
161
+ )
162
+
163
+ TransferFlags = bitmask(FFI::Type::UINT16, [
164
+ :LINKED,
165
+ :PENDING,
166
+ :POST_PENDING_TRANSFER,
167
+ :VOID_PENDING_TRANSFER,
168
+ :BALANCING_DEBIT,
169
+ :BALANCING_CREDIT,
170
+ :CLOSING_DEBIT,
171
+ :CLOSING_CREDIT,
172
+ :IMPORTED]
173
+ )
174
+
175
+ AccountFilterFlags = bitmask(FFI::Type::UINT32, [:DEBITS, :CREDITS, :REVERSED])
176
+
177
+ QueryFilterFlags = bitmask(FFI::Type::UINT32, [:REVERSED])
178
+
179
+ class Client < FFI::Struct
180
+ layout opaque: [:uint64, 4]
181
+ end
182
+
183
+ class UInt128 < FFI::Struct
184
+ layout low: :uint64,
185
+ high: :uint64
186
+ end
187
+
188
+ class Packet < FFI::Struct
189
+ layout user_data: :pointer,
190
+ data: :pointer,
191
+ data_size: :uint32,
192
+ user_tag: :uint16,
193
+ operation: Operation,
194
+ status: PacketStatus,
195
+ opaque: [:uint8, 64]
196
+ end
197
+
198
+ class Account < FFI::Struct
199
+ layout id: UInt128,
200
+ debits_pending: UInt128,
201
+ debits_posted: UInt128,
202
+ credits_pending: UInt128,
203
+ credits_posted: UInt128,
204
+ user_data_128: UInt128,
205
+ user_data_64: :uint64,
206
+ user_data_32: :uint32,
207
+ reserved: :uint32,
208
+ ledger: :uint32,
209
+ code: :uint16,
210
+ flags: AccountFlags,
211
+ timestamp: :uint64
212
+ end
213
+
214
+ class Transfer < FFI::Struct
215
+ layout id: UInt128,
216
+ debit_account_id: UInt128,
217
+ credit_account_id: UInt128,
218
+ amount: UInt128,
219
+ pending_id: UInt128,
220
+ user_data_128: UInt128,
221
+ user_data_64: :uint64,
222
+ user_data_32: :uint32,
223
+ timeout: :uint32,
224
+ ledger: :uint32,
225
+ code: :uint16,
226
+ flags: TransferFlags,
227
+ timestamp: :uint64
228
+ end
229
+
230
+ class CreateAccountsResult < FFI::Struct
231
+ layout index: :uint32,
232
+ result: CreateAccountResult
233
+ end
234
+
235
+ class CreateTransfersResult < FFI::Struct
236
+ layout index: :uint32,
237
+ result: CreateTransferResult
238
+ end
239
+
240
+ class AccountFilter < FFI::Struct
241
+ layout account_id: UInt128,
242
+ user_data_128: UInt128,
243
+ user_data_64: :uint64,
244
+ user_data_32: :uint32,
245
+ code: :uint16,
246
+ reserved: [:uint8, 58],
247
+ timestamp_min: :uint64,
248
+ timestamp_max: :uint64,
249
+ limit: :uint32,
250
+ flags: AccountFilterFlags
251
+ end
252
+
253
+ class QueryFilter < FFI::Struct
254
+ layout user_data_128: UInt128,
255
+ user_data_64: :uint64,
256
+ user_data_32: :uint32,
257
+ ledger: :uint32,
258
+ code: :uint16,
259
+ reserved: [:uint8, 6],
260
+ timestamp_min: :uint64,
261
+ timestamp_max: :uint64,
262
+ limit: :uint32,
263
+ flags: QueryFilterFlags
264
+ end
265
+
266
+ class AccountBalance < FFI::Struct
267
+ layout debits_pending: UInt128,
268
+ debits_posted: UInt128,
269
+ credits_pending: UInt128,
270
+ credits_posted: UInt128,
271
+ timestamp: :uint64,
272
+ reserved: [:uint8, 56]
273
+ end
274
+
275
+ callback :on_completion, [:uint, Packet.by_ref, :uint64, :pointer, :uint32], :void
276
+ callback :log_handler, [LogLevel, :pointer, :uint32], :void
277
+
278
+ attach_function :tb_client_init, [Client.by_ref, :pointer, :string, :uint32, :uint, :on_completion], InitStatus
279
+ attach_function :tb_client_submit, [Client.by_ref, Packet.by_ref], ClientStatus
280
+ attach_function :tb_client_deinit, [Client.by_ref], ClientStatus
281
+ attach_function :tb_client_register_log_callback, [:log_handler, :bool], RegisterLogCallbackStatus
282
+ end
@@ -0,0 +1,38 @@
1
+ module TigerBeetle
2
+ ACCOUNT_PARAMS = %i[
3
+ id debits_pending debits_posted credits_pending credits_posted user_data_128
4
+ user_data_64 user_data_32 ledger code flags timestamp
5
+ ]
6
+
7
+ Account = Struct.new(*ACCOUNT_PARAMS) do
8
+ def initialize(
9
+ id:,
10
+ debits_pending: 0,
11
+ debits_posted: 0,
12
+ credits_pending: 0,
13
+ credits_posted: 0,
14
+ user_data_128: 0,
15
+ user_data_64: 0,
16
+ user_data_32: 0,
17
+ ledger:,
18
+ code:,
19
+ flags: [],
20
+ timestamp: nil
21
+ )
22
+ super(
23
+ id,
24
+ debits_pending,
25
+ debits_posted,
26
+ credits_pending,
27
+ credits_posted,
28
+ user_data_128,
29
+ user_data_64,
30
+ user_data_32,
31
+ ledger,
32
+ code,
33
+ flags,
34
+ timestamp
35
+ )
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ module TigerBeetle
2
+ ACCOUNT_BALANCE_PARAMS = %i[
3
+ debits_pending debits_posted credits_pending credits_posted timestamp
4
+ ]
5
+
6
+ AccountBalance = Struct.new(*ACCOUNT_BALANCE_PARAMS) do
7
+ def initialize(
8
+ debits_pending: 0,
9
+ debits_posted: 0,
10
+ credits_pending: 0,
11
+ credits_posted: 0,
12
+ timestamp: 0
13
+ )
14
+ super(
15
+ debits_pending,
16
+ debits_posted,
17
+ credits_pending,
18
+ credits_posted,
19
+ timestamp
20
+ )
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ module TigerBeetle
2
+ ACCOUNT_FILTER_PARAMS = %i[
3
+ account_id user_data_128 user_data_64 user_data_32 code timestamp_min timestamp_max limit flags
4
+ ]
5
+
6
+ AccountFilter = Struct.new(*ACCOUNT_FILTER_PARAMS) do
7
+ def initialize(
8
+ account_id:,
9
+ user_data_128: 0,
10
+ user_data_64: 0,
11
+ user_data_32: 0,
12
+ code: 0,
13
+ timestamp_min: 0,
14
+ timestamp_max: 0,
15
+ limit:,
16
+ flags: []
17
+ )
18
+ super(
19
+ account_id,
20
+ user_data_128,
21
+ user_data_64,
22
+ user_data_32,
23
+ code,
24
+ timestamp_min,
25
+ timestamp_max,
26
+ limit,
27
+ flags
28
+ )
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,14 @@
1
+ module TigerBeetle
2
+ class AtomicCounter
3
+ attr_reader :value
4
+
5
+ def initialize(value = 0)
6
+ @value = value
7
+ @mutex = Mutex.new
8
+ end
9
+
10
+ def increment
11
+ @mutex.synchronize { @value += 1 }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,214 @@
1
+ require 'ffi'
2
+ require 'tb_client'
3
+ require 'tigerbeetle/converters'
4
+ require 'tigerbeetle/account'
5
+ require 'tigerbeetle/account_balance'
6
+ require 'tigerbeetle/account_filter'
7
+ require 'tigerbeetle/atomic_counter'
8
+ require 'tigerbeetle/error'
9
+ require 'tigerbeetle/query_filter'
10
+ require 'tigerbeetle/request'
11
+ require 'tigerbeetle/transfer'
12
+
13
+ module TigerBeetle
14
+ class Client
15
+ def self.next_id
16
+ @counter ||= AtomicCounter.new
17
+ @counter.increment
18
+ end
19
+
20
+ def initialize(cluster_id = 0, address = '3000')
21
+ @inflight_requests = {}
22
+ # Keep a pointer to prevent proc from getting GCed
23
+ @callback = Proc.new { |*args| callback(*args) }
24
+ @client_id = self.class.next_id
25
+ @client = TBClient::Client.new
26
+
27
+ # FFI::MemoryPointer.new(:pointer, 1) # **void
28
+ cluster_id_ptr = serialize([cluster_id], Converters::UInt128)
29
+
30
+ status = TBClient.tb_client_init(
31
+ @client,
32
+ cluster_id_ptr,
33
+ address,
34
+ address.length,
35
+ @client_id,
36
+ &@callback
37
+ )
38
+ raise ClientError, "Unable to initialize native client: #{status}" unless status == :SUCCESS
39
+
40
+ # Make sure to deinitialize all clients when the process terminates
41
+ at_exit { deinit }
42
+ end
43
+
44
+ def logger=(logger)
45
+ @logger = logger
46
+ @log_callback = @logger ? Proc.new { |*args| log_callback(*args) } : nil
47
+
48
+ status = TBClient.tb_client_register_log_callback(@log_callback, true)
49
+ raise ClientError, "Unable to register logger: #{status}" unless status == :SUCCESS
50
+ end
51
+
52
+ def create_accounts(*accounts, &block)
53
+ submit_request(
54
+ :CREATE_ACCOUNTS,
55
+ accounts,
56
+ Converters::Account,
57
+ Converters::CreateAccountsResult,
58
+ &block
59
+ )
60
+ end
61
+
62
+ def create_transfers(*transfers, &block)
63
+ submit_request(
64
+ :CREATE_TRANSFERS,
65
+ transfers,
66
+ Converters::Transfer,
67
+ Converters::CreateTransfersResult,
68
+ &block
69
+ )
70
+ end
71
+
72
+ def lookup_accounts(*account_ids, &block)
73
+ submit_request(
74
+ :LOOKUP_ACCOUNTS,
75
+ account_ids,
76
+ Converters::UInt128,
77
+ Converters::Account,
78
+ &block
79
+ )
80
+ end
81
+
82
+ def lookup_transfers(*transfer_ids, &block)
83
+ submit_request(
84
+ :LOOKUP_TRANSFERS,
85
+ transfer_ids,
86
+ Converters::UInt128,
87
+ Converters::Transfer,
88
+ &block
89
+ )
90
+ end
91
+
92
+ def get_account_transfers(filter, &block)
93
+ submit_request(
94
+ :GET_ACCOUNT_TRANSFERS,
95
+ [filter],
96
+ Converters::AccountFilter,
97
+ Converters::Transfer,
98
+ &block
99
+ )
100
+ end
101
+
102
+ def get_account_balances(filter, &block)
103
+ submit_request(
104
+ :GET_ACCOUNT_BALANCES,
105
+ [filter],
106
+ Converters::AccountFilter,
107
+ Converters::AccountBalance,
108
+ &block
109
+ )
110
+ end
111
+
112
+ def query_accounts(filter, &block)
113
+ submit_request(
114
+ :QUERY_ACCOUNTS,
115
+ [filter],
116
+ Converters::QueryFilter,
117
+ Converters::Account,
118
+ &block
119
+ )
120
+ end
121
+
122
+ def query_transfers(filter, &block)
123
+ submit_request(
124
+ :QUERY_TRANSFERS,
125
+ [filter],
126
+ Converters::QueryFilter,
127
+ Converters::Transfer,
128
+ &block
129
+ )
130
+ end
131
+
132
+ def deinit
133
+ return unless client
134
+ return unless inflight_requests.empty?
135
+
136
+ TBClient.tb_client_deinit(client)
137
+ @client = nil
138
+ end
139
+
140
+ private
141
+
142
+ attr_reader :context, :client, :inflight_requests, :logger
143
+
144
+ def log_callback(level, ptr, length)
145
+ message = ptr.read_string(length).force_encoding('UTF-8')
146
+ case level
147
+ when :ERR then logger.error(message)
148
+ when :WARN then logger.warn(message)
149
+ when :INFO then logger.info(message)
150
+ when :DEBUG then logger.debug(message)
151
+ end
152
+ end
153
+
154
+ def callback(client_id, packet, timestamp, result_ptr, result_len)
155
+ request_id = packet[:user_data].read_uint64
156
+ request = inflight_requests.delete(request_id)
157
+ result = deserialize(result_ptr, request.converter, result_len)
158
+ request.block.call(result)
159
+ end
160
+
161
+ def submit_request(operation, request, request_converter, response_converter, &block)
162
+ raise ClientError, 'Client is not connected' unless client
163
+
164
+ request_id = self.class.next_id
165
+ user_data_ptr = FFI::MemoryPointer.new(:uint64, 1)
166
+ user_data_ptr.write_uint64(request_id)
167
+
168
+ data_ptr = serialize(request, request_converter)
169
+
170
+ packet = TBClient::Packet.new
171
+ packet[:user_data] = user_data_ptr
172
+ packet[:operation] = operation
173
+ packet[:status] = :OK
174
+ packet[:data_size] = data_ptr.size
175
+ packet[:data] = data_ptr
176
+
177
+ queue = Queue.new
178
+ inflight_requests[request_id] = Request.new(packet, response_converter) do |response|
179
+ if block
180
+ block.call(response)
181
+ else
182
+ queue << response
183
+ end
184
+ end
185
+
186
+ status = TBClient.tb_client_submit(client, packet)
187
+ raise ClientError, "Unable to submit request: #{status}" unless status == :OK
188
+
189
+ # block until the client return a response
190
+ queue.pop unless block
191
+ end
192
+
193
+ def serialize(data, converter)
194
+ data_ptr = FFI::MemoryPointer.new(converter.native_type, data.length, true)
195
+ data.each_with_index do |value, i|
196
+ # initialize type at the memory address and write value over it
197
+ converter.to_native(data_ptr[i], value)
198
+ end
199
+
200
+ data_ptr
201
+ end
202
+
203
+ def deserialize(ptr, converter, length)
204
+ type = converter.native_type
205
+ # copy data to own memory to retain it after tb_client has freed its memory
206
+ own_ptr = FFI::MemoryPointer.new(:char, type.size * length)
207
+ own_ptr.put_bytes(0, ptr.read_bytes(type.size * length))
208
+
209
+ Array.new(length / type.size) do |i|
210
+ converter.from_native(own_ptr + i * type.size)
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,63 @@
1
+ require 'tb_client'
2
+ require 'tigerbeetle/account'
3
+ require 'tigerbeetle/converters/base'
4
+ require 'tigerbeetle/converters/time'
5
+ require 'tigerbeetle/converters/uint_128'
6
+
7
+ module TigerBeetle
8
+ module Converters
9
+ class Account < Base
10
+ def self.native_type
11
+ TBClient::Account
12
+ end
13
+
14
+ def from_native(ptr)
15
+ c_value = TBClient::Account.new(ptr)
16
+
17
+ TigerBeetle::Account.new(
18
+ id: Converters::UInt128.from_native(c_value[:id].to_ptr),
19
+ debits_pending: Converters::UInt128.from_native(c_value[:debits_pending].to_ptr),
20
+ debits_posted: Converters::UInt128.from_native(c_value[:debits_posted].to_ptr),
21
+ credits_pending: Converters::UInt128.from_native(c_value[:credits_pending].to_ptr),
22
+ credits_posted: Converters::UInt128.from_native(c_value[:credits_posted].to_ptr),
23
+ user_data_128: Converters::UInt128.from_native(c_value[:user_data_128].to_ptr),
24
+ user_data_64: c_value[:user_data_64],
25
+ user_data_32: c_value[:user_data_32],
26
+ ledger: c_value[:ledger],
27
+ code: c_value[:code],
28
+ flags: c_value[:flags],
29
+ timestamp: Converters::Time.from_native(ptr + c_value.offset_of(:timestamp))
30
+ )
31
+ end
32
+
33
+ def to_native(ptr, value)
34
+ validate_uint!(:id, 128, value.id)
35
+ validate_uint!(:debits_pending, 128, value.debits_pending)
36
+ validate_uint!(:debits_posted, 128, value.debits_posted)
37
+ validate_uint!(:credits_pending, 128, value.credits_pending)
38
+ validate_uint!(:credits_posted, 128, value.credits_posted)
39
+ validate_uint!(:user_data_128, 128, value.user_data_128)
40
+ validate_uint!(:user_data_64, 64, value.user_data_64)
41
+ validate_uint!(:user_data_32, 32, value.user_data_32)
42
+ validate_uint!(:ledger, 32, value.ledger)
43
+ validate_uint!(:code, 16, value.code)
44
+
45
+ TBClient::Account.new(ptr).tap do |result|
46
+ Converters::UInt128.to_native(result[:id].to_ptr, value.id)
47
+ Converters::UInt128.to_native(result[:debits_pending].to_ptr, value.debits_pending)
48
+ Converters::UInt128.to_native(result[:debits_posted].to_ptr, value.debits_posted)
49
+ Converters::UInt128.to_native(result[:credits_pending].to_ptr, value.credits_pending)
50
+ Converters::UInt128.to_native(result[:credits_posted].to_ptr, value.credits_posted)
51
+ Converters::UInt128.to_native(result[:user_data_128].to_ptr, value.user_data_128)
52
+ result[:user_data_64] = value.user_data_64
53
+ result[:user_data_32] = value.user_data_32
54
+ result[:reserved] = 0
55
+ result[:ledger] = value.ledger
56
+ result[:code] = value.code
57
+ result[:flags] = value.flags
58
+ Converters::Time.to_native(ptr + result.offset_of(:timestamp), value.timestamp || 0)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,31 @@
1
+ require 'tb_client'
2
+ require 'tigerbeetle/account_balance'
3
+ require 'tigerbeetle/converters/base'
4
+ require 'tigerbeetle/converters/time'
5
+ require 'tigerbeetle/converters/uint_128'
6
+
7
+ module TigerBeetle
8
+ module Converters
9
+ class AccountBalance < Base
10
+ def self.native_type
11
+ TBClient::AccountBalance
12
+ end
13
+
14
+ def from_native(ptr)
15
+ c_value = TBClient::AccountBalance.new(ptr)
16
+
17
+ TigerBeetle::AccountBalance.new(
18
+ debits_pending: Converters::UInt128.from_native(c_value[:debits_pending].to_ptr),
19
+ debits_posted: Converters::UInt128.from_native(c_value[:debits_posted].to_ptr),
20
+ credits_pending: Converters::UInt128.from_native(c_value[:credits_pending].to_ptr),
21
+ credits_posted: Converters::UInt128.from_native(c_value[:credits_posted].to_ptr),
22
+ timestamp: Converters::Time.from_native(ptr + c_value.offset_of(:timestamp))
23
+ )
24
+ end
25
+
26
+ def to_native(ptr, value)
27
+ raise 'Unexpected conversion of AccountBalance to native type'
28
+ end
29
+ end
30
+ end
31
+ end