hakuban 0.6.5 → 0.7.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.
data/lib/hakuban/ffi.rb CHANGED
@@ -1,174 +1,250 @@
1
1
  require 'json'
2
2
  require 'ffi'
3
+ require 'hakuban/ffi-object'
3
4
 
4
5
 
5
- # all functions which can trigger synchronuous callbacks, and those which wait on rust locks are "blocking: true" to avoid deadlock on GIL
6
6
  module Hakuban::FFI
7
7
 
8
- class FFILocalNodeNewResult < FFI::Struct
9
- layout :error, :uint8, :local_node_pointer, :pointer
10
- end
11
-
12
- class FFIObjectObserveResult < FFI::Struct
13
- layout :error, :uint8, :object_observe_pointer, :pointer
14
- end
15
-
16
- class FFIObjectExposeResult < FFI::Struct
17
- layout :error, :uint8, :object_expose_pointer, :pointer
18
- end
19
-
20
- class FFITagObserveResult < FFI::Struct
21
- layout :error, :uint8, :tag_observe_pointer, :pointer
22
- end
23
-
24
- class FFITagExposeResult < FFI::Struct
25
- layout :error, :uint8, :tag_expose_pointer, :pointer
26
- end
27
-
28
- class FFIObjectObserveStateGetVersionResult < FFI::Struct
29
- layout :version_length, :size_t, :version_elements, :pointer
30
- end
8
+ extend FFI::Library
9
+ ffi_lib 'hakuban'
10
+
11
+
12
+ class FFIError < Exception; end
13
+ class FFIErrorInvalidString < FFIError; end
14
+ class FFIErrorInvalidJSON < FFIError; end
15
+ class FFIErrorInvalidURL < FFIError; end
16
+ class FFIErrorInvalidLogLevel < FFIError; end
17
+ class FFIErrorUnknownError < FFIError; end
31
18
 
32
- class FFIObjectObserveStateGetDataTypeResult < FFI::Struct
33
- layout :type_length, :size_t, :type_elements, :pointer
19
+ class FFIResultStatus < FFI::Struct
20
+ layout :id, :uint8
21
+ ENUM = [:Ok, :Pending, :NotAvailable, :InvalidString, :InvalidJSON, :InvalidURL, :InvalidLogLevel, :UnknownError]
22
+
23
+ def to_sym
24
+ ENUM[self[:id]]
25
+ end
26
+
27
+ def to_exception
28
+ Hakuban::FFI::const_get("FFIError"+ENUM[self[:id]].to_s)
29
+ end
34
30
  end
35
31
 
36
- class FFIObjectObserveStateGetDataResult < FFI::Struct
37
- layout :data_length, :size_t, :data_bytes, :pointer
32
+
33
+ class FFIResult < FFI::Struct
34
+ FFIResultStatus::ENUM.each { |sym|
35
+ define_method(("is_"+sym.to_s+"?").to_sym) {
36
+ FFIResultStatus::ENUM[self[:status][:id]] == sym
37
+ }
38
+ }
38
39
  end
39
40
 
40
- class FFIObjectExposeState < FFI::Struct
41
- layout :version_length, :size_t, :version, :pointer, :data_type_length, :size_t, :data_type, :pointer, :raw_length, :size_t, :raw, :pointer
42
- attr_accessor :data_type_strings
43
-
44
- def self.construct(version, data_type, data)
45
- state = FFIObjectExposeState::new
46
- state[:version_length] = version.size
47
- state[:version] = FFI::MemoryPointer.new(:int64, version.size)
48
- state[:version].write_array_of_int64(version)
49
- state[:data_type_length] = data_type.size
50
- state[:data_type] = FFI::MemoryPointer.new(:pointer, data_type.size)
51
- state.data_type_strings = data_type.map {|string| FFI::MemoryPointer.from_string(string)}
52
- state[:data_type].write_array_of_pointer(state.data_type_strings)
53
- state[:raw] = FFI::MemoryPointer.from_string(data)
54
- state[:raw_length] = data.size
55
- state
41
+
42
+ class FFIResultWithNothing < FFIResult
43
+ layout :status, FFIResultStatus
44
+
45
+ def unwrap
46
+ return nil if self[:status].to_sym == :NotAvailable
47
+ raise self[:status].to_exception if self[:status].to_sym != :Ok
48
+ nil
56
49
  end
57
50
  end
58
-
59
- class FFIObjectExposeStateResult < FFI::Struct
60
- layout :error, :uint8, :changed, :uint8
61
- end
62
-
63
- class FFITokioWebsocketConnectorNewResult < FFI::Struct
64
- layout :error, :uint8, :websocket_connector_pointer, :pointer
51
+
52
+
53
+ class FFIResultWithPointer < FFIResult
54
+ layout :status, FFIResultStatus, :pointer, :pointer
55
+
56
+ def unwrap
57
+ return nil if self[:status].to_sym == :NotAvailable
58
+ raise self[:status].to_exception if self[:status].to_sym != :Ok
59
+ self[:pointer]
60
+ end
65
61
  end
66
62
 
67
- class FFITagObserveObjectStateBorrowResult < FFI::Struct
68
- layout :error, :uint8, :state, :pointer
69
- end
70
63
 
71
- class FFIObjectDescriptor < FFI::Struct
72
- layout :tags_count, :size_t, :tags, :pointer, :json, :pointer
73
- attr_accessor :tags_strings
74
-
75
- def tags
76
- self[:tags].read_array_of_pointer(self[:tags_count]).map { |string| JSON.parse(string.read_string()) } # does this copy the string?
64
+ class Callbacks
65
+
66
+ attr_reader :waker
67
+
68
+ def initialize
69
+ @queues = {}
70
+ @queues_sequence = 0
71
+ @queues_mutex = Mutex.new
72
+ @waker = proc { |pointer|
73
+ queue_id = pointer.address
74
+ @queues_mutex.synchronize {
75
+ @queues[queue_id]&.each { |queue|
76
+ queue << :wake
77
+ }
78
+ }
79
+ }
77
80
  end
78
-
79
- def json
80
- JSON.parse(self[:json].read_string())
81
+
82
+ def register_queue(queue)
83
+ @queues_mutex.synchronize {
84
+ @queues_sequence = (@queues_sequence+1) % 2**32
85
+ queue_id = @queues_sequence
86
+ (@queues[queue_id] ||= Set.new) << queue
87
+ queue_id
88
+ }
81
89
  end
82
-
83
- def self.construct(tags,json)
84
- descriptor = FFIObjectDescriptor.new
85
- descriptor[:tags_count] = tags.size
86
- descriptor.tags_strings = tags.map { |tag| FFI::MemoryPointer.from_string(JSON.dump(tag)) }
87
- descriptor[:tags] = FFI::MemoryPointer.new(:pointer, descriptor.tags_strings.size)
88
- descriptor[:tags].write_array_of_pointer(descriptor.tags_strings)
89
- descriptor[:json] = FFI::MemoryPointer.from_string(JSON.dump(json))
90
- descriptor
90
+
91
+ def unregister_queue(queue_id)
92
+ @queues_mutex.synchronize {
93
+ @queues[queue_id].delete(queue_id)
94
+ @queues.delete(queue_id) if @queues[queue_id].empty?
95
+ }
91
96
  end
92
-
93
97
  end
98
+
94
99
 
95
- class FFITagDescriptor < FFI::Struct
96
- layout :json, :pointer
97
-
98
- def json
99
- JSON.parse(self[:json].read_string())
100
- end
101
-
102
- def self.construct(json)
103
- descriptor = FFITagDescriptor.new
104
- descriptor[:json] = FFI::MemoryPointer.from_string(JSON.dump(json))
105
- descriptor
100
+ class FFIFutureReturningNothing < FFI::Struct
101
+ layout :pointer, :pointer
102
+
103
+ @@callbacks = Callbacks.new
104
+
105
+ def self.create_and_await(owner)
106
+ Thread.handle_interrupt(Object => :never) {
107
+ begin
108
+ queue = queue_id = drop_locked = pointer = nil
109
+ Thread.handle_interrupt(Object => :immediate) {
110
+ queue = Queue.new
111
+ queue_id = @@callbacks.register_queue(queue)
112
+ drop_locked = owner.drop_lock(queue)
113
+ return nil if !pointer = owner.with_pointer { |pointer| yield pointer }
114
+ loop {
115
+ result = owner.with_pointer { Hakuban::FFI::hakuban_future_returning_nothing_poll(pointer[:pointer],@@callbacks.waker,::FFI::Pointer.new(queue_id)) }
116
+ return result if not result.is_Pending?
117
+ # Currently the only future returning nothing is guaranteed to complete at first poll
118
+ # :unreachable:
119
+ raise Hakuban::FFIObject::PointerAlreadyDropped if queue.pop == :dropping
120
+ # :unreachable:
121
+ }
122
+ }
123
+ ensure
124
+ Hakuban::FFI::hakuban_future_returning_nothing_drop(pointer[:pointer]) if pointer
125
+ owner.drop_release(queue) if drop_locked
126
+ @@callbacks.unregister_queue(queue_id)
127
+ end
128
+ }
106
129
  end
107
-
108
130
  end
109
-
110
-
111
- class FFIObjectDescriptors < FFI::Struct
112
- layout :count, :size_t, :descriptors, :pointer
113
-
114
- def descriptors
115
- self[:count].times.map { |i| FFIObjectDescriptor.new(self[:descriptors] + i * FFIObjectDescriptor.size) }
131
+
132
+
133
+ class FFIFutureReturningPointer < FFI::Struct
134
+ layout :pointer, :pointer
135
+
136
+ @@callbacks = Callbacks.new
137
+
138
+ def self.create_and_await(owner)
139
+ Thread.handle_interrupt(Object => :never) {
140
+ begin
141
+ queue = queue_id = drop_locked = pointer = nil
142
+ Thread.handle_interrupt(Object => :immediate) {
143
+ queue = Queue.new
144
+ queue_id = @@callbacks.register_queue(queue)
145
+ drop_locked = owner.drop_lock(queue)
146
+ return nil if !pointer = owner.with_pointer { |pointer| yield pointer }
147
+ loop {
148
+ result = owner.with_pointer { Hakuban::FFI::hakuban_future_returning_pointer_poll(pointer[:pointer],@@callbacks.waker,::FFI::Pointer.new(queue_id)) }
149
+ return result if not result.is_Pending?
150
+ raise Hakuban::FFIObject::PointerAlreadyDropped if queue.pop == :dropping
151
+ }
152
+ }
153
+ ensure
154
+ Hakuban::FFI::hakuban_future_returning_pointer_drop(pointer[:pointer]) if pointer
155
+ owner.drop_release(queue) if drop_locked
156
+ @@callbacks.unregister_queue(queue_id)
157
+ end
158
+ }
116
159
  end
117
160
  end
161
+
162
+
163
+ class FFIArray < FFI::Struct
164
+ layout :length, :size_t, :pointer, :pointer
165
+ end
118
166
 
119
167
 
120
- extend FFI::Library
168
+ # Every function which can cause a wake() has to be "blocking: true".
169
+ # Otherwise they are very likely to deadlock on GVL and other locks acquired by RemoteExchange on incomming message.
170
+ # FIXME: way too many functions are marked as blocking here
121
171
 
122
- ffi_lib 'hakuban'
172
+ callback :waker, [ :pointer ], :void
123
173
 
124
- callback :object_callback, [ :pointer, FFIObjectDescriptor.by_value, :uint8 ], :void
125
- #callback :tag_callback, [ :pointer, FFITagDescriptor.by_value, :uint8 ], :void
126
-
127
- attach_function :hakuban_local_node_new, [ :string ], FFILocalNodeNewResult.by_value
128
- attach_function :hakuban_local_node_drop, [ :pointer ], :void, blocking: true
129
-
130
- attach_function :hakuban_object_observe_new, [ :pointer, FFIObjectDescriptor.by_value ], FFIObjectObserveResult.by_value, blocking: true
131
- attach_function :hakuban_object_observe_drop, [ :pointer ], :void, blocking: true
132
- attach_function :hakuban_object_observe_state_borrow, [ :pointer ], :pointer, blocking: true
133
- attach_function :hakuban_object_observe_events_get, [ :pointer ], :pointer, blocking: true
134
-
135
- attach_function :hakuban_object_expose_new, [ :pointer, FFIObjectDescriptor.by_value], FFIObjectExposeResult.by_value, blocking: true
136
- attach_function :hakuban_object_expose_drop, [ :pointer ], :void, blocking: true
137
- attach_function :hakuban_object_expose_state, [ :pointer, FFIObjectExposeState.by_value, :uint64 ], FFIObjectExposeStateResult.by_value, blocking: true
138
- attach_function :hakuban_object_expose_assignment, [ :pointer ], :uint64, blocking: true
139
- attach_function :hakuban_object_expose_desynchronize, [ :pointer, :uint64 ], :void, blocking: true
140
- attach_function :hakuban_object_expose_events_get, [ :pointer ], :pointer, blocking: true
141
-
142
- attach_function :hakuban_tag_observe_new, [ :pointer, FFITagDescriptor.by_value ], FFITagObserveResult.by_value, blocking: true
143
- attach_function :hakuban_tag_observe_drop, [ :pointer ], :void, blocking: true
144
- attach_function :hakuban_tag_observe_object_descriptors_borrow, [ :pointer ], FFIObjectDescriptors.by_value, blocking: true
145
- attach_function :hakuban_tag_observe_object_state_borrow, [ :pointer, FFIObjectDescriptor.by_value ], FFITagObserveObjectStateBorrowResult.by_value, blocking: true
146
- attach_function :hakuban_tag_observe_events_get, [ :pointer ], :pointer, blocking: true
147
-
148
- attach_function :hakuban_tag_expose_new, [ :pointer, FFITagDescriptor.by_value ], FFITagExposeResult.by_value, blocking: true
149
- attach_function :hakuban_tag_expose_drop, [ :pointer ], :void, blocking: true
150
- attach_function :hakuban_tag_expose_object_descriptors_borrow, [ :pointer ], FFIObjectDescriptors.by_value, blocking: true
151
- attach_function :hakuban_tag_expose_object_state, [ :pointer, FFIObjectDescriptor.by_value, FFIObjectExposeState.by_value, :uint64 ], FFIObjectExposeStateResult.by_value, blocking: true
152
- attach_function :hakuban_tag_expose_object_assignment, [ :pointer, FFIObjectDescriptor.by_value ], :uint64, blocking: true
153
- attach_function :hakuban_tag_expose_object_desynchronize, [ :pointer, FFIObjectDescriptor.by_value, :uint64 ], :void, blocking: true
154
- attach_function :hakuban_tag_expose_events_get, [ :pointer ], :pointer, blocking: true
155
-
156
- attach_function :hakuban_object_observe_state_get_synchronized, [ :pointer ], :uint64
157
- attach_function :hakuban_object_observe_state_get_data_version, [ :pointer ], FFIObjectObserveStateGetVersionResult.by_value
158
- attach_function :hakuban_object_observe_state_get_data_type, [ :pointer ], FFIObjectObserveStateGetDataTypeResult.by_value
159
- attach_function :hakuban_object_observe_state_get_data, [ :pointer ], FFIObjectObserveStateGetDataResult.by_value
160
-
161
- attach_function :hakuban_object_descriptor_events_callback_register, [ :pointer, :object_callback, :pointer ], :pointer, blocking: true
162
- attach_function :hakuban_object_descriptor_events_callback_unregister, [ :pointer ], :void, blocking: true
163
- attach_function :hakuban_object_descriptor_events_return, [ :pointer ], :void, blocking: true
164
-
165
- attach_function :hakuban_object_observe_state_return, [ :pointer ], :void
166
- attach_function :hakuban_object_descriptors_return, [ FFIObjectDescriptors.by_value ], :void
174
+ attach_function :hakuban_logger_initialize, [ :string ], FFIResultWithNothing.by_value
167
175
 
168
- attach_function :hakuban_tokio_init_multi_thread, [ :size_t ], :pointer
169
- attach_function :hakuban_tokio_websocket_connector_new, [ :pointer, :pointer, :string ], FFITokioWebsocketConnectorNewResult.by_value
170
- attach_function :hakuban_tokio_websocket_connector_drop, [ :pointer, :pointer ], :void, blocking: true
176
+ attach_function :hakuban_local_exchange_new, [ :string ], FFIResultWithPointer.by_value
177
+ attach_function :hakuban_local_exchange_drop, [ :pointer ], :void
178
+ attach_function :hakuban_local_exchange_clone, [ :pointer ], :pointer
171
179
 
172
- attach_function :hakuban_logger_initialize, [ :string ], :uint8
180
+ attach_function :hakuban_tokio_init_multi_thread, [ :size_t ], :pointer
181
+ attach_function :hakuban_tokio_websocket_connector_new, [ :pointer, :pointer, :string ], FFIResultWithPointer.by_value
182
+ attach_function :hakuban_tokio_websocket_connector_drop, [ :pointer ], :void, blocking: true
183
+
184
+ attach_function :hakuban_object_observe_contract_new, [ :pointer, :pointer ], :pointer, blocking: true
185
+ attach_function :hakuban_object_observe_contract_drop, [ :pointer ], :void, blocking: true
186
+ attach_function :hakuban_object_observe_contract_terminate, [ :pointer ], :void, blocking: true
187
+ attach_function :hakuban_object_observe_contract_next, [ :pointer ], FFIFutureReturningPointer.by_value, blocking: true
188
+ attach_function :hakuban_object_observe_contract_ready, [ :pointer ], FFIResultWithPointer.by_value, blocking: true
189
+
190
+ attach_function :hakuban_object_expose_contract_new, [ :pointer, :pointer ], :pointer, blocking: true
191
+ attach_function :hakuban_object_expose_contract_drop, [ :pointer ], :void, blocking: true
192
+ attach_function :hakuban_object_expose_contract_terminate, [ :pointer ], :void, blocking: true
193
+ attach_function :hakuban_object_expose_contract_next, [ :pointer ], FFIFutureReturningPointer.by_value, blocking: true
194
+ attach_function :hakuban_object_expose_contract_ready, [ :pointer ], FFIResultWithPointer.by_value, blocking: true
195
+
196
+ attach_function :hakuban_tag_observe_contract_new, [ :pointer, :pointer ], :pointer, blocking: true
197
+ attach_function :hakuban_tag_observe_contract_drop, [ :pointer ], :void, blocking: true
198
+ attach_function :hakuban_tag_observe_contract_terminate, [ :pointer ], :void, blocking: true
199
+ attach_function :hakuban_tag_observe_contract_next, [ :pointer ], FFIFutureReturningPointer.by_value, blocking: true
200
+ attach_function :hakuban_tag_observe_contract_ready, [ :pointer ], FFIArray.by_value, blocking: true
201
+
202
+ attach_function :hakuban_tag_expose_contract_new, [ :pointer, :pointer ], :pointer, blocking: true
203
+ attach_function :hakuban_tag_expose_contract_drop, [ :pointer ], :void, blocking: true
204
+ attach_function :hakuban_tag_expose_contract_terminate, [ :pointer ], :void, blocking: true
205
+ attach_function :hakuban_tag_expose_contract_next, [ :pointer ], FFIFutureReturningPointer.by_value, blocking: true
206
+ attach_function :hakuban_tag_expose_contract_ready, [ :pointer ], FFIArray.by_value, blocking: true
207
+
208
+ attach_function :hakuban_object_state_stream_drop, [ :pointer ], :void, blocking: true
209
+ attach_function :hakuban_object_state_stream_next, [ :pointer ], FFIFutureReturningPointer.by_value, blocking: true
210
+ attach_function :hakuban_object_state_stream_current, [ :pointer ], FFIResultWithPointer.by_value
211
+ attach_function :hakuban_object_state_stream_descriptor, [ :pointer ], :pointer
212
+
213
+ attach_function :hakuban_object_state_sink_drop, [ :pointer ], :void, blocking: true
214
+ attach_function :hakuban_object_state_sink_next, [ :pointer ], FFIFutureReturningPointer.by_value, blocking: true
215
+ attach_function :hakuban_object_state_sink_send, [ :pointer, :pointer ], FFIFutureReturningNothing.by_value, blocking: true
216
+ attach_function :hakuban_object_state_sink_current, [ :pointer ], FFIResultWithPointer.by_value, blocking: true
217
+ attach_function :hakuban_object_state_sink_descriptor, [ :pointer ], :pointer
218
+ attach_function :hakuban_object_state_sink_desynchronize, [ :pointer ], :void, blocking: true
219
+
220
+ attach_function :hakuban_object_state_sink_params_drop, [ :pointer ], :void
221
+ attach_function :hakuban_object_state_sink_params_clone, [ :pointer ], :pointer
222
+
223
+ attach_function :hakuban_object_state_new, [ :size_t, :pointer, :size_t, :pointer, :size_t, :pointer, :uint64 ], FFIResultWithPointer.by_value
224
+ attach_function :hakuban_object_state_drop, [ :pointer ], :void
225
+ attach_function :hakuban_object_state_clone, [ :pointer ], :pointer
226
+ attach_function :hakuban_object_state_data, [ :pointer ], FFIArray.by_value
227
+ attach_function :hakuban_object_state_format, [ :pointer ], FFIArray.by_value
228
+ attach_function :hakuban_object_state_version, [ :pointer ], FFIArray.by_value
229
+ attach_function :hakuban_object_state_synchronized_ago, [ :pointer ], :uint64
230
+
231
+ attach_function :hakuban_tag_descriptor_new, [ :string ], FFIResultWithPointer.by_value
232
+ attach_function :hakuban_tag_descriptor_drop, [ :pointer ], :void
233
+ attach_function :hakuban_tag_descriptor_clone, [ :pointer ], :pointer
234
+ attach_function :hakuban_tag_descriptor_json, [ :pointer ], :string
235
+
236
+ attach_function :hakuban_object_descriptor_new, [ :string, :size_t, :pointer ], FFIResultWithPointer.by_value
237
+ attach_function :hakuban_object_descriptor_drop, [ :pointer ], :void
238
+ attach_function :hakuban_object_descriptor_clone, [ :pointer ], :pointer
239
+ attach_function :hakuban_object_descriptor_json, [ :pointer ], :string
240
+ attach_function :hakuban_object_descriptor_tags, [ :pointer ], FFIArray.by_value
241
+
242
+ attach_function :hakuban_future_returning_nothing_drop, [ :pointer ], :void, blocking: true
243
+ attach_function :hakuban_future_returning_pointer_drop, [ :pointer ], :void, blocking: true
244
+ attach_function :hakuban_future_returning_nothing_poll, [ :pointer, :waker, :pointer ], FFIResultWithNothing.by_value, blocking: true
245
+ attach_function :hakuban_future_returning_pointer_poll, [ :pointer, :waker, :pointer ], FFIResultWithPointer.by_value, blocking: true
246
+
247
+ attach_function :hakuban_array_drop, [ FFIArray.by_value ], :void
173
248
 
174
249
  end
250
+
@@ -0,0 +1,16 @@
1
+ module Hakuban
2
+
3
+ @@logger_initialized = false
4
+
5
+ def self.logger_initialize(default_level, skip_if_already_initialized: false)
6
+ Hakuban::hakuban_initialize
7
+ if @@logger_initialized and !skip_if_already_initialized
8
+ raise "Logger already initialized. This can't be done more than once. Make sure logger_initialize is called before any LocalExchange gets constructed."
9
+ end
10
+ if not @@logger_initialized
11
+ raise "Invalid default log level string" if ! FFI::hakuban_logger_initialize(default_level).is_Ok?
12
+ @@logger_initialized = true
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,106 @@
1
+ require 'hakuban/ffi-object.rb'
2
+
3
+
4
+
5
+ module Hakuban
6
+
7
+ class ObjectState < FFIObject
8
+
9
+ class WrongFormatError < Exception; end
10
+
11
+ def initialize(data, version: nil, format: nil, synchronized_us_ago: 0, &block)
12
+ if version.nil?
13
+ timestamp = Time.new
14
+ version = [0, timestamp.to_i, timestamp.nsec]
15
+ end
16
+ @version = version
17
+ @format = [format || []].flatten
18
+ @synchronized_us_ago = synchronized_us_ago
19
+ @data = data
20
+ @pointer = false
21
+ @mutex = Mutex.new
22
+ self.do_and_drop_or_return(&block)
23
+ end
24
+
25
+
26
+ def with_pointer
27
+ @mutex.synchronize {
28
+ raise PointerAlreadyDropped if dropped?
29
+ if @pointer == false
30
+ Hakuban::hakuban_initialize
31
+ version_pointer = ::FFI::MemoryPointer.new(:int64, @version.size)
32
+ version_pointer.write_array_of_int64(@version)
33
+ format_pointer = ::FFI::MemoryPointer.new(:pointer, @format.size)
34
+ format_strings = format.map {|string| ::FFI::MemoryPointer.from_string(string)}
35
+ format_pointer.write_array_of_pointer(format_strings)
36
+ data_pointer = ::FFI::MemoryPointer.from_string(@data)
37
+ pointer = FFI::hakuban_object_state_new(@version.size, version_pointer, @format.size, format_pointer, @data.bytesize, data_pointer, @synchronized_us_ago).unwrap
38
+ initialize_pointer(pointer, :hakuban_object_state_drop, :hakuban_object_state_clone)
39
+ end
40
+ yield ::FFI::Pointer.new(@pointer)
41
+ }
42
+ end
43
+
44
+
45
+ private_class_method def self.from_ffi_pointer(pointer)
46
+ new_instance = allocate
47
+ new_instance.send(:initialize_pointer, pointer, :hakuban_object_state_drop, :hakuban_object_state_clone)
48
+ new_instance
49
+ end
50
+
51
+ def data
52
+ return @data if defined? @data
53
+ array = with_pointer { |pointer| FFI::hakuban_object_state_data(pointer) }
54
+ #array[:data_bytes].read_array_of_char(array[:length]) #.pack('c*').clone()
55
+ @data = array[:pointer].read_string(array[:length]).clone()
56
+ end
57
+
58
+ def format
59
+ return @format if defined? @format
60
+ array = with_pointer { |pointer| FFI::hakuban_object_state_format(pointer) }
61
+ @format = array[:pointer].read_array_of_pointer(array[:length]).map { |string| string.read_string().clone() }
62
+ end
63
+
64
+ def version
65
+ return @version if defined? @version
66
+ array = with_pointer { |pointer| FFI::hakuban_object_state_version(pointer) }
67
+ @version = array[:pointer].read_array_of_int64(array[:length])
68
+ end
69
+
70
+ def synchronized_us_ago
71
+ return @synchronized_us_ago if defined? @synchronized_us_ago
72
+ @synchronized_us_ago = with_pointer { |pointer| FFI::hakuban_object_state_synchronized_ago(pointer) }
73
+ end
74
+
75
+ def with_data(data)
76
+ ObjectState.new(data, version: version, format: format, synchronized_us_ago: synchronized_us_ago)
77
+ end
78
+
79
+ def with_version(version)
80
+ ObjectState.new(data, version: version, format: format, synchronized_us_ago: synchronized_us_ago)
81
+ end
82
+
83
+ def with_format(format)
84
+ ObjectState.new(data, version: version, format: format, synchronized_us_ago: synchronized_us_ago)
85
+ end
86
+
87
+ def with_synchronized_us_ago(synchronized_us_ago)
88
+ ObjectState.new(data, version: version, format: format, synchronized_us_ago: synchronized_us_ago)
89
+ end
90
+
91
+ def json_deserialize
92
+ raise WrongFormatError if format[-1] != "JSON"
93
+ ObjectState.new(JSON.parse(data), version: version, format: format[0...-1], synchronized_us_ago: synchronized_us_ago)
94
+ end
95
+
96
+ def json_serialize
97
+ ObjectState.new(JSON.dump(data), version: version, format: format+["JSON"], synchronized_us_ago: synchronized_us_ago)
98
+ end
99
+
100
+ def inspect
101
+ "#<ObjectState @synchronized=%p @version=%p>"%[synchronized_us_ago, version]
102
+ end
103
+
104
+ end
105
+
106
+ end
@@ -0,0 +1,64 @@
1
+ require 'hakuban/ffi-object.rb'
2
+ require 'hakuban/stream.rb'
3
+
4
+
5
+ module Hakuban
6
+
7
+ class ObjectStateSink < FFIObject
8
+
9
+ private_class_method :new
10
+
11
+ def initialize(pointer)
12
+ initialize_pointer(pointer, :hakuban_object_state_sink_drop, nil)
13
+ end
14
+
15
+
16
+ include Stream
17
+
18
+
19
+ def next(&block)
20
+ return nil if ! pointer = FFI::FFIFutureReturningPointer.create_and_await(self) { |pointer| FFI::hakuban_object_state_sink_next(pointer) }.unwrap
21
+ ObjectStateSinkParams.send(:new, pointer).do_and_drop_or_return(&block)
22
+ rescue FFIObject::PointerAlreadyDropped
23
+ end
24
+
25
+
26
+ def current(&block)
27
+ return nil if ! pointer = with_pointer { |pointer| FFI::hakuban_object_state_sink_current(pointer) }.unwrap
28
+ ObjectStateSinkParams.send(:new, pointer).do_and_drop_or_return(&block)
29
+ rescue FFIObject::PointerAlreadyDropped
30
+ end
31
+
32
+
33
+ def send(object_state)
34
+ FFI::FFIFutureReturningNothing.create_and_await(self) { |pointer|
35
+ object_state.with_pointer { |object_state_pointer| FFI::hakuban_object_state_sink_send(pointer, object_state_pointer) }
36
+ }.unwrap
37
+ rescue FFIObject::PointerAlreadyDropped
38
+ end
39
+
40
+
41
+ def descriptor
42
+ Hakuban::ObjectDescriptor.send(:from_ffi_pointer, with_pointer { |pointer| FFI::hakuban_object_state_sink_descriptor(pointer) })
43
+ end
44
+
45
+
46
+ def desynchronize
47
+ with_pointer { |pointer| FFI::hakuban_object_state_sink_desynchronize(pointer) }
48
+ rescue FFIObject::PointerAlreadyDropped
49
+ end
50
+
51
+ end
52
+
53
+ class ObjectStateSinkParams < FFIObject
54
+
55
+ private_class_method :new
56
+
57
+ def initialize(pointer)
58
+ initialize_pointer(pointer, :hakuban_object_state_sink_params_drop, :hakuban_object_state_sink_params_clone)
59
+ end
60
+
61
+ end
62
+
63
+
64
+ end
@@ -0,0 +1,39 @@
1
+ require 'hakuban/ffi-object.rb'
2
+ require 'hakuban/stream.rb'
3
+
4
+
5
+ module Hakuban
6
+
7
+ class ObjectStateStream < FFIObject
8
+
9
+ private_class_method :new
10
+
11
+ def initialize(pointer)
12
+ initialize_pointer(pointer, :hakuban_object_state_stream_drop, nil)
13
+ end
14
+
15
+
16
+ include Stream
17
+
18
+
19
+ def next(&block)
20
+ return nil if ! pointer = FFI::FFIFutureReturningPointer.create_and_await(self) { |pointer| FFI::hakuban_object_state_stream_next(pointer) }.unwrap
21
+ ObjectState.send(:from_ffi_pointer, pointer).do_and_drop_or_return(&block)
22
+ rescue FFIObject::PointerAlreadyDropped
23
+ end
24
+
25
+
26
+ def current(&block)
27
+ return nil if ! pointer = with_pointer { |pointer| FFI::hakuban_object_state_stream_current(pointer) }.unwrap
28
+ ObjectState.send(:from_ffi_pointer, pointer).do_and_drop_or_return(&block)
29
+ rescue FFIObject::PointerAlreadyDropped
30
+ end
31
+
32
+
33
+ def descriptor
34
+ Hakuban::ObjectDescriptor.send(:from_ffi_pointer, with_pointer { |pointer| FFI::hakuban_object_state_stream_descriptor(pointer) })
35
+ end
36
+
37
+ end
38
+
39
+ end