unibuf 0.1.0 → 0.1.2

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +178 -330
  3. data/CODE_OF_CONDUCT.md +132 -0
  4. data/README.adoc +443 -254
  5. data/docs/CAPNPROTO.adoc +436 -0
  6. data/docs/FLATBUFFERS.adoc +430 -0
  7. data/docs/PROTOBUF.adoc +515 -0
  8. data/docs/TXTPROTO.adoc +369 -0
  9. data/lib/unibuf/commands/convert.rb +60 -2
  10. data/lib/unibuf/commands/schema.rb +68 -11
  11. data/lib/unibuf/errors.rb +23 -26
  12. data/lib/unibuf/models/capnproto/enum_definition.rb +72 -0
  13. data/lib/unibuf/models/capnproto/field_definition.rb +81 -0
  14. data/lib/unibuf/models/capnproto/interface_definition.rb +70 -0
  15. data/lib/unibuf/models/capnproto/method_definition.rb +81 -0
  16. data/lib/unibuf/models/capnproto/schema.rb +84 -0
  17. data/lib/unibuf/models/capnproto/struct_definition.rb +96 -0
  18. data/lib/unibuf/models/capnproto/union_definition.rb +62 -0
  19. data/lib/unibuf/models/flatbuffers/enum_definition.rb +69 -0
  20. data/lib/unibuf/models/flatbuffers/field_definition.rb +88 -0
  21. data/lib/unibuf/models/flatbuffers/schema.rb +102 -0
  22. data/lib/unibuf/models/flatbuffers/struct_definition.rb +70 -0
  23. data/lib/unibuf/models/flatbuffers/table_definition.rb +73 -0
  24. data/lib/unibuf/models/flatbuffers/union_definition.rb +60 -0
  25. data/lib/unibuf/models/message.rb +10 -0
  26. data/lib/unibuf/models/values/scalar_value.rb +2 -2
  27. data/lib/unibuf/parsers/binary/wire_format_parser.rb +199 -19
  28. data/lib/unibuf/parsers/capnproto/binary_parser.rb +267 -0
  29. data/lib/unibuf/parsers/capnproto/grammar.rb +272 -0
  30. data/lib/unibuf/parsers/capnproto/list_reader.rb +208 -0
  31. data/lib/unibuf/parsers/capnproto/pointer_decoder.rb +163 -0
  32. data/lib/unibuf/parsers/capnproto/processor.rb +348 -0
  33. data/lib/unibuf/parsers/capnproto/segment_reader.rb +131 -0
  34. data/lib/unibuf/parsers/capnproto/struct_reader.rb +199 -0
  35. data/lib/unibuf/parsers/flatbuffers/binary_parser.rb +325 -0
  36. data/lib/unibuf/parsers/flatbuffers/grammar.rb +235 -0
  37. data/lib/unibuf/parsers/flatbuffers/processor.rb +299 -0
  38. data/lib/unibuf/parsers/textproto/grammar.rb +1 -1
  39. data/lib/unibuf/parsers/textproto/processor.rb +10 -0
  40. data/lib/unibuf/serializers/binary_serializer.rb +218 -0
  41. data/lib/unibuf/serializers/capnproto/binary_serializer.rb +402 -0
  42. data/lib/unibuf/serializers/capnproto/list_writer.rb +199 -0
  43. data/lib/unibuf/serializers/capnproto/pointer_encoder.rb +118 -0
  44. data/lib/unibuf/serializers/capnproto/segment_builder.rb +124 -0
  45. data/lib/unibuf/serializers/capnproto/struct_writer.rb +139 -0
  46. data/lib/unibuf/serializers/flatbuffers/binary_serializer.rb +167 -0
  47. data/lib/unibuf/validators/type_validator.rb +1 -1
  48. data/lib/unibuf/version.rb +1 -1
  49. data/lib/unibuf.rb +27 -0
  50. metadata +36 -1
@@ -0,0 +1,402 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "segment_builder"
4
+ require_relative "pointer_encoder"
5
+ require_relative "struct_writer"
6
+ require_relative "list_writer"
7
+
8
+ module Unibuf
9
+ module Serializers
10
+ module Capnproto
11
+ # Serializer for Cap'n Proto binary format
12
+ # Coordinates segment building, pointer encoding, and data writing
13
+ class BinarySerializer
14
+ attr_reader :schema, :segment_builder
15
+
16
+ # Initialize with schema
17
+ # @param schema [Models::Capnproto::Schema] Cap'n Proto schema
18
+ def initialize(schema)
19
+ @schema = schema
20
+ @segment_builder = SegmentBuilder.new
21
+ end
22
+
23
+ # Serialize data to binary format
24
+ # @param data [Hash] Data to serialize
25
+ # @param root_type [String, nil] Root struct type name
26
+ # @return [String] Binary data
27
+ def serialize(data, root_type: nil)
28
+ # Determine root type from schema if not provided
29
+ root_type ||= @schema.structs.first&.name
30
+ raise SerializationError, "No root type specified" unless root_type
31
+
32
+ struct_def = @schema.find_struct(root_type)
33
+ unless struct_def
34
+ raise SerializationError,
35
+ "Struct type not found: #{root_type}"
36
+ end
37
+
38
+ # Allocate root pointer (1 word at position 0)
39
+ @segment_builder.allocate(1)
40
+
41
+ # Write root struct and get its location
42
+ root_location = write_struct(data, struct_def)
43
+
44
+ # Write root pointer at position 0
45
+ # Root pointer points to position 1 (offset = 0 from position 1)
46
+ pointer = PointerEncoder.encode_struct(
47
+ 0, # offset from pointer position (1) to struct (1)
48
+ root_location[:data_words],
49
+ root_location[:pointer_words],
50
+ )
51
+ @segment_builder.write_word(0, 0, pointer)
52
+
53
+ # Build final binary
54
+ @segment_builder.build
55
+ end
56
+
57
+ private
58
+
59
+ # Write a struct
60
+ # @param data [Hash] Struct data
61
+ # @param struct_def [Models::Capnproto::StructDefinition] Struct definition
62
+ # @return [Hash] Location info
63
+ def write_struct(data, struct_def)
64
+ # Count data and pointer words needed
65
+ data_words = calculate_data_words(struct_def)
66
+ pointer_words = calculate_pointer_words(struct_def)
67
+
68
+ # Allocate space for struct
69
+ segment_id, word_offset = @segment_builder.allocate(data_words + pointer_words)
70
+
71
+ # Create struct writer
72
+ struct_writer = StructWriter.new(
73
+ @segment_builder,
74
+ segment_id,
75
+ word_offset,
76
+ data_words,
77
+ pointer_words,
78
+ )
79
+
80
+ # Write each field
81
+ struct_def.fields.each do |field|
82
+ value = data[field.name.to_sym] || data[field.name]
83
+ next unless value
84
+
85
+ write_field(struct_writer, field, value, struct_def)
86
+ end
87
+
88
+ {
89
+ segment_id: segment_id,
90
+ word_offset: word_offset,
91
+ data_words: data_words,
92
+ pointer_words: pointer_words,
93
+ }
94
+ end
95
+
96
+ # Write a field
97
+ def write_field(struct_writer, field, value, struct_def)
98
+ if field.primitive_type?
99
+ write_primitive_field(struct_writer, field, value)
100
+ elsif field.list_type?
101
+ write_list_field(struct_writer, field, value, struct_def)
102
+ elsif text_or_data_type?(field)
103
+ write_text_or_data_field(struct_writer, field, value, struct_def)
104
+ elsif field.user_type?
105
+ write_user_type_field(struct_writer, field, value, struct_def)
106
+ end
107
+ end
108
+
109
+ # Write a primitive field
110
+ def write_primitive_field(struct_writer, field, value)
111
+ ordinal = field.ordinal
112
+ type = field.type
113
+
114
+ case type
115
+ when "Bool"
116
+ struct_writer.write_bool(ordinal / 64, ordinal % 64, value)
117
+ when "Int8"
118
+ struct_writer.write_int8(ordinal / 8, ordinal % 8, value)
119
+ when "UInt8"
120
+ struct_writer.write_uint8(ordinal / 8, ordinal % 8, value)
121
+ when "Int16"
122
+ struct_writer.write_int16(ordinal / 4, ordinal % 4, value)
123
+ when "UInt16"
124
+ struct_writer.write_uint16(ordinal / 4, ordinal % 4, value)
125
+ when "Int32"
126
+ struct_writer.write_int32(ordinal / 2, ordinal % 2, value)
127
+ when "UInt32"
128
+ struct_writer.write_uint32(ordinal / 2, ordinal % 2, value)
129
+ when "Int64"
130
+ struct_writer.write_int64(ordinal, value)
131
+ when "UInt64"
132
+ struct_writer.write_uint64(ordinal, value)
133
+ when "Float32"
134
+ struct_writer.write_float32(ordinal / 2, ordinal % 2, value)
135
+ when "Float64"
136
+ struct_writer.write_float64(ordinal, value)
137
+ end
138
+ end
139
+
140
+ # Write a list field
141
+ def write_list_field(struct_writer, field, value, struct_def)
142
+ return if value.nil? || value.empty?
143
+
144
+ element_type = field.element_type
145
+
146
+ # Handle Text specially
147
+ if element_type == "Text"
148
+ write_text_field(struct_writer, field, value, struct_def)
149
+ return
150
+ end
151
+
152
+ # Determine element size
153
+ element_size = PointerEncoder.element_size_for_type(element_type)
154
+ element_count = value.length
155
+
156
+ # Allocate space for list
157
+ words_needed = calculate_list_words(element_size, element_count)
158
+ segment_id, word_offset = @segment_builder.allocate(words_needed)
159
+
160
+ # Create list writer
161
+ list_writer = ListWriter.new(
162
+ @segment_builder,
163
+ segment_id,
164
+ word_offset,
165
+ element_size,
166
+ element_count,
167
+ )
168
+
169
+ # Write elements
170
+ value.each_with_index do |elem, i|
171
+ if primitive_type?(element_type)
172
+ type_symbol = type_to_symbol(element_type)
173
+ list_writer.write_primitive(i, elem, type_symbol)
174
+ else
175
+ # Handle struct elements (more complex, simplified for now)
176
+ # Would need to recursively write structs
177
+ end
178
+ end
179
+
180
+ # Get pointer index for this field
181
+ pointer_index = get_pointer_index(field, struct_def)
182
+
183
+ # Calculate offset from pointer position to list
184
+ pointer_pos = struct_writer.pointer_position(pointer_index)
185
+ offset = word_offset - pointer_pos - 1
186
+
187
+ # Write list pointer
188
+ pointer = PointerEncoder.encode_list(offset, element_size,
189
+ element_count)
190
+ struct_writer.write_pointer(pointer_index, pointer)
191
+ end
192
+
193
+ # Write Text or Data field
194
+ def write_text_or_data_field(struct_writer, field, value, struct_def)
195
+ if field.type == "Text"
196
+ write_text_field(struct_writer, field, value, struct_def)
197
+ else
198
+ # Data field - similar to text but no encoding
199
+ write_data_field(struct_writer, field, value, struct_def)
200
+ end
201
+ end
202
+
203
+ # Write data field
204
+ def write_data_field(struct_writer, field, value, struct_def)
205
+ # Data is a byte list
206
+ element_count = value.bytesize
207
+
208
+ # Calculate words needed
209
+ words_needed = (element_count + 7) / 8
210
+ segment_id, word_offset = @segment_builder.allocate(words_needed)
211
+
212
+ # Create list writer
213
+ list_writer = ListWriter.new(
214
+ @segment_builder,
215
+ segment_id,
216
+ word_offset,
217
+ PointerEncoder::ELEMENT_SIZE_BYTE,
218
+ element_count,
219
+ )
220
+
221
+ # Write data
222
+ list_writer.write_data(value)
223
+
224
+ # Get pointer index for this field
225
+ pointer_index = get_pointer_index(field, struct_def)
226
+
227
+ # Calculate offset and write pointer
228
+ pointer_pos = struct_writer.pointer_position(pointer_index)
229
+ offset = word_offset - pointer_pos - 1
230
+
231
+ pointer = PointerEncoder.encode_list(offset,
232
+ PointerEncoder::ELEMENT_SIZE_BYTE, element_count)
233
+ struct_writer.write_pointer(pointer_index, pointer)
234
+ end
235
+
236
+ # Write text field
237
+ def write_text_field(struct_writer, field, value, struct_def)
238
+ # Text is a byte list with null terminator
239
+ bytes = value.bytes + [0]
240
+ element_count = bytes.length
241
+
242
+ # Calculate words needed
243
+ words_needed = (element_count + 7) / 8
244
+ segment_id, word_offset = @segment_builder.allocate(words_needed)
245
+
246
+ # Create list writer
247
+ list_writer = ListWriter.new(
248
+ @segment_builder,
249
+ segment_id,
250
+ word_offset,
251
+ PointerEncoder::ELEMENT_SIZE_BYTE,
252
+ element_count,
253
+ )
254
+
255
+ # Write text
256
+ list_writer.write_text(value)
257
+
258
+ # Get pointer index for this field
259
+ pointer_index = get_pointer_index(field, struct_def)
260
+
261
+ # Calculate offset and write pointer
262
+ pointer_pos = struct_writer.pointer_position(pointer_index)
263
+ offset = word_offset - pointer_pos - 1
264
+
265
+ pointer = PointerEncoder.encode_list(offset,
266
+ PointerEncoder::ELEMENT_SIZE_BYTE, element_count)
267
+ struct_writer.write_pointer(pointer_index, pointer)
268
+ end
269
+
270
+ # Write user-defined type field
271
+ def write_user_type_field(struct_writer, field, value, struct_def)
272
+ # Check if it's an enum
273
+ enum_def = @schema.find_enum(field.type)
274
+ if enum_def
275
+ # Enums are stored as UInt16
276
+ ordinal_value = if value.is_a?(String) || value.is_a?(Symbol)
277
+ enum_def.values[value.to_s]
278
+ else
279
+ value
280
+ end
281
+ struct_writer.write_uint16(field.ordinal / 4, field.ordinal % 4,
282
+ ordinal_value || 0)
283
+ else
284
+ # It's a struct - write recursively
285
+ nested_struct_def = @schema.find_struct(field.type)
286
+ return unless nested_struct_def
287
+
288
+ nested_location = write_struct(value, nested_struct_def)
289
+
290
+ # Get pointer index for this field
291
+ pointer_index = get_pointer_index(field, struct_def)
292
+
293
+ # Calculate offset and write pointer
294
+ pointer_pos = struct_writer.pointer_position(pointer_index)
295
+ offset = nested_location[:word_offset] - pointer_pos - 1
296
+
297
+ pointer = PointerEncoder.encode_struct(
298
+ offset,
299
+ nested_location[:data_words],
300
+ nested_location[:pointer_words],
301
+ )
302
+ struct_writer.write_pointer(pointer_index, pointer)
303
+ end
304
+ end
305
+
306
+ # Get pointer index for a field
307
+ # Count non-primitive fields before this one
308
+ def get_pointer_index(field, struct_def)
309
+ struct_def.fields.take_while do |f|
310
+ f != field
311
+ end.count { |f| !f.primitive_type? }
312
+ end
313
+
314
+ # Helper methods
315
+ def calculate_data_words(struct_def)
316
+ # In Cap'n Proto, we need to count actual data words based on field types
317
+ # Group fields by their size and pack them efficiently
318
+ max_word = 0
319
+
320
+ struct_def.fields.each do |field|
321
+ next unless field.primitive_type?
322
+
323
+ word_index = case field.type
324
+ when "Bool"
325
+ field.ordinal / 64 # 64 bools per word
326
+ when "Int8", "UInt8"
327
+ field.ordinal / 8 # 8 bytes per word
328
+ when "Int16", "UInt16"
329
+ field.ordinal / 4 # 4 shorts per word
330
+ when "Int32", "UInt32", "Float32"
331
+ field.ordinal / 2 # 2 ints per word
332
+ when "Int64", "UInt64", "Float64"
333
+ field.ordinal # 1 long per word
334
+ else
335
+ 0
336
+ end
337
+
338
+ max_word = [max_word, word_index].max
339
+ end
340
+
341
+ max_word + 1
342
+ end
343
+
344
+ def calculate_pointer_words(struct_def)
345
+ # Pointer fields use separate ordinals
346
+ # Count fields that are NOT primitives
347
+ pointer_fields = struct_def.fields.reject(&:primitive_type?)
348
+ return 0 if pointer_fields.empty?
349
+
350
+ # Get max pointer ordinal
351
+ max_pointer_ordinal = pointer_fields.map(&:ordinal).max
352
+ max_pointer_ordinal + 1
353
+ end
354
+
355
+ def calculate_list_words(element_size, count)
356
+ case element_size
357
+ when PointerEncoder::ELEMENT_SIZE_VOID
358
+ 0
359
+ when PointerEncoder::ELEMENT_SIZE_BIT
360
+ (count + 63) / 64
361
+ when PointerEncoder::ELEMENT_SIZE_BYTE
362
+ (count + 7) / 8
363
+ when PointerEncoder::ELEMENT_SIZE_TWO_BYTES
364
+ (count + 3) / 4
365
+ when PointerEncoder::ELEMENT_SIZE_FOUR_BYTES
366
+ (count + 1) / 2
367
+ when PointerEncoder::ELEMENT_SIZE_EIGHT_BYTES, PointerEncoder::ELEMENT_SIZE_POINTER
368
+ count
369
+ else
370
+ count # Inline composite
371
+ end
372
+ end
373
+
374
+ def primitive_type?(type)
375
+ Models::Capnproto::FieldDefinition::PRIMITIVE_TYPES.include?(type)
376
+ end
377
+
378
+ def type_to_symbol(type)
379
+ case type
380
+ when "Int8" then :int8
381
+ when "UInt8" then :uint8
382
+ when "Int16" then :int16
383
+ when "UInt16" then :uint16
384
+ when "Int32" then :int32
385
+ when "UInt32" then :uint32
386
+ when "Int64" then :int64
387
+ when "UInt64" then :uint64
388
+ when "Float32" then :float32
389
+ when "Float64" then :float64
390
+ when "Bool" then :bool
391
+ else :uint64
392
+ end
393
+ end
394
+
395
+ # Check if field is Text or Data type
396
+ def text_or_data_type?(field)
397
+ ["Text", "Data"].include?(field.type)
398
+ end
399
+ end
400
+ end
401
+ end
402
+ end
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "segment_builder"
4
+ require_relative "pointer_encoder"
5
+
6
+ module Unibuf
7
+ module Serializers
8
+ module Capnproto
9
+ # Writer for Cap'n Proto list data
10
+ # Handles lists of primitives, pointers, and structs
11
+ class ListWriter
12
+ attr_reader :segment_builder, :segment_id, :word_offset, :element_size,
13
+ :element_count
14
+
15
+ # Initialize list writer
16
+ # @param segment_builder [SegmentBuilder] Segment builder
17
+ # @param segment_id [Integer] Segment ID
18
+ # @param word_offset [Integer] Word offset in segment
19
+ # @param element_size [Integer] Element size code
20
+ # @param element_count [Integer] Number of elements
21
+ def initialize(segment_builder, segment_id, word_offset, element_size,
22
+ element_count)
23
+ @segment_builder = segment_builder
24
+ @segment_id = segment_id
25
+ @word_offset = word_offset
26
+ @element_size = element_size
27
+ @element_count = element_count
28
+ end
29
+
30
+ # Write a primitive element
31
+ # @param index [Integer] Element index
32
+ # @param value [Object] Element value
33
+ # @param type [Symbol] Value type
34
+ def write_primitive(index, value, type = :uint64)
35
+ raise ArgumentError, "Index out of bounds" if index >= @element_count
36
+
37
+ case @element_size
38
+ when PointerEncoder::ELEMENT_SIZE_VOID
39
+ # Void - nothing to write
40
+ when PointerEncoder::ELEMENT_SIZE_BIT
41
+ write_bit(index, value)
42
+ when PointerEncoder::ELEMENT_SIZE_BYTE
43
+ write_byte(index, value, type)
44
+ when PointerEncoder::ELEMENT_SIZE_TWO_BYTES
45
+ write_two_bytes(index, value, type)
46
+ when PointerEncoder::ELEMENT_SIZE_FOUR_BYTES
47
+ write_four_bytes(index, value, type)
48
+ when PointerEncoder::ELEMENT_SIZE_EIGHT_BYTES
49
+ write_eight_bytes(index, value, type)
50
+ else
51
+ raise "Cannot write primitive to this list type"
52
+ end
53
+ end
54
+
55
+ # Write a pointer element
56
+ # @param index [Integer] Element index
57
+ # @param pointer_word [Integer] Encoded pointer word
58
+ def write_pointer(index, pointer_word)
59
+ raise ArgumentError, "Index out of bounds" if index >= @element_count
60
+ raise "List elements are not pointers" unless @element_size == PointerEncoder::ELEMENT_SIZE_POINTER
61
+
62
+ @segment_builder.write_word(@segment_id, @word_offset + index,
63
+ pointer_word)
64
+ end
65
+
66
+ # Write text (UTF-8 string) as byte list
67
+ # @param text [String] Text to write
68
+ def write_text(text)
69
+ raise "Not a byte list" unless @element_size == PointerEncoder::ELEMENT_SIZE_BYTE
70
+
71
+ bytes = text.bytes + [0] # Add null terminator
72
+ bytes.each_with_index do |byte, i|
73
+ write_byte(i, byte, :uint8) if i < @element_count
74
+ end
75
+ end
76
+
77
+ # Write data (binary) as byte list
78
+ # @param data [String] Binary data to write
79
+ def write_data(data)
80
+ raise "Not a byte list" unless @element_size == PointerEncoder::ELEMENT_SIZE_BYTE
81
+
82
+ data.bytes.each_with_index do |byte, i|
83
+ write_byte(i, byte, :uint8) if i < @element_count
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def write_bit(index, value)
90
+ word_index = index / 64
91
+ bit_index = index % 64
92
+
93
+ all_segments = @segment_builder.segments
94
+ current = if @segment_id < all_segments.length
95
+ all_segments[@segment_id][@word_offset + word_index] || 0
96
+ else
97
+ 0
98
+ end
99
+
100
+ new_value = if value
101
+ current | (1 << bit_index)
102
+ else
103
+ current & ~(1 << bit_index)
104
+ end
105
+
106
+ @segment_builder.write_word(@segment_id, @word_offset + word_index,
107
+ new_value)
108
+ end
109
+
110
+ def write_byte(index, value, type)
111
+ word_index = index / 8
112
+ byte_index = index % 8
113
+
114
+ all_segments = @segment_builder.segments
115
+ current = if @segment_id < all_segments.length
116
+ all_segments[@segment_id][@word_offset + word_index] || 0
117
+ else
118
+ 0
119
+ end
120
+
121
+ # Clear the byte
122
+ mask = 0xFF << (byte_index * 8)
123
+ cleared = current & ~mask
124
+
125
+ # Set new value
126
+ byte_value = type == :int8 && value.negative? ? value + 256 : value
127
+ new_value = cleared | ((byte_value & 0xFF) << (byte_index * 8))
128
+
129
+ @segment_builder.write_word(@segment_id, @word_offset + word_index,
130
+ new_value)
131
+ end
132
+
133
+ def write_two_bytes(index, value, type)
134
+ word_index = index / 4
135
+ half_word_index = index % 4
136
+
137
+ all_segments = @segment_builder.segments
138
+ current = if @segment_id < all_segments.length
139
+ all_segments[@segment_id][@word_offset + word_index] || 0
140
+ else
141
+ 0
142
+ end
143
+
144
+ # Clear the half-word
145
+ mask = 0xFFFF << (half_word_index * 16)
146
+ cleared = current & ~mask
147
+
148
+ # Set new value
149
+ short_value = type == :int16 && value.negative? ? value + 65536 : value
150
+ new_value = cleared | ((short_value & 0xFFFF) << (half_word_index * 16))
151
+
152
+ @segment_builder.write_word(@segment_id, @word_offset + word_index,
153
+ new_value)
154
+ end
155
+
156
+ def write_four_bytes(index, value, type)
157
+ word_index = index / 2
158
+ dword_index = index % 2
159
+
160
+ all_segments = @segment_builder.segments
161
+ current = if @segment_id < all_segments.length
162
+ all_segments[@segment_id][@word_offset + word_index] || 0
163
+ else
164
+ 0
165
+ end
166
+
167
+ # Clear the dword
168
+ mask = 0xFFFFFFFF << (dword_index * 32)
169
+ cleared = current & ~mask
170
+
171
+ # Set new value
172
+ int_value = if type == :float32
173
+ [value].pack("f").unpack1("L")
174
+ else
175
+ type == :int32 && value.negative? ? value + 4294967296 : value
176
+ end
177
+
178
+ new_value = cleared | ((int_value & 0xFFFFFFFF) << (dword_index * 32))
179
+
180
+ @segment_builder.write_word(@segment_id, @word_offset + word_index,
181
+ new_value)
182
+ end
183
+
184
+ def write_eight_bytes(index, value, type)
185
+ word_value = if type == :float64
186
+ [value].pack("d").unpack1("Q")
187
+ elsif type == :int64 && value.negative?
188
+ value + 18446744073709551616
189
+ else
190
+ value
191
+ end
192
+
193
+ @segment_builder.write_word(@segment_id, @word_offset + index,
194
+ word_value & 0xFFFFFFFFFFFFFFFF)
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end