hakuban 0.6.4 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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