Skip to content

Commit 878d845

Browse files
committed
Treat yp_buffer_t as an opaque pointer
Right now, we have to keep the buffer FFI object in sync with the definition of yp_buffer_t because we access its fields directly. If we add more fields or change the order, things will get out of sync. Instead, let's treat yp_buffer_t as an opaque pointer and access its fields through accessor functions directly. This is more consistent with how we handle strings anyway.
1 parent eff49c9 commit 878d845

File tree

4 files changed

+90
-49
lines changed

4 files changed

+90
-49
lines changed

ext/yarp/extension.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ dump_input(yp_string_t *input, const char *filepath) {
6666
yp_node_t *node = yp_parse(&parser);
6767
yp_serialize(&parser, node, &buffer);
6868

69-
VALUE result = rb_str_new(buffer.value, buffer.length);
69+
VALUE result = rb_str_new(yp_buffer_value(&buffer), yp_buffer_length(&buffer));
7070
yp_node_destroy(&parser, node);
7171
yp_buffer_free(&buffer);
7272
yp_parser_free(&parser);
@@ -483,7 +483,7 @@ parse_serialize_file_metadata(VALUE self, VALUE filepath, VALUE metadata) {
483483
if (!yp_string_mapped_init(&input, checked)) return Qnil;
484484

485485
yp_parse_serialize(yp_string_source(&input), yp_string_length(&input), &buffer, check_string(metadata));
486-
VALUE result = rb_str_new(buffer.value, buffer.length);
486+
VALUE result = rb_str_new(yp_buffer_value(&buffer), yp_buffer_length(&buffer));
487487

488488
yp_buffer_free(&buffer);
489489
return result;

include/yarp/util/yp_buffer.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,24 @@
1212
// A yp_buffer_t is a simple memory buffer that stores data in a contiguous
1313
// block of memory. It is used to store the serialized representation of a
1414
// YARP tree.
15-
// NOTE: keep in sync with YARP::LibRubyParser::Buffer in lib/yarp.rb
1615
typedef struct {
1716
char *value;
1817
size_t length;
1918
size_t capacity;
2019
} yp_buffer_t;
2120

21+
// Return the size of the yp_buffer_t struct.
22+
YP_EXPORTED_FUNCTION size_t yp_buffer_sizeof(void);
23+
2224
// Initialize a yp_buffer_t with its default values.
2325
YP_EXPORTED_FUNCTION bool yp_buffer_init(yp_buffer_t *buffer);
2426

27+
// Return the value of the buffer.
28+
YP_EXPORTED_FUNCTION char * yp_buffer_value(yp_buffer_t *buffer);
29+
30+
// Return the length of the buffer.
31+
YP_EXPORTED_FUNCTION size_t yp_buffer_length(yp_buffer_t *buffer);
32+
2533
// Append the given amount of space as zeroes to the buffer.
2634
void yp_buffer_append_zeroes(yp_buffer_t *buffer, size_t length);
2735

lib/yarp/ffi.rb

Lines changed: 61 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,10 @@ def self.load_exported_functions_from(header, *functions)
7575

7676
load_exported_functions_from(
7777
"yarp/util/yp_buffer.h",
78+
"yp_buffer_sizeof",
7879
"yp_buffer_init",
80+
"yp_buffer_value",
81+
"yp_buffer_length",
7982
"yp_buffer_free"
8083
)
8184

@@ -88,34 +91,49 @@ def self.load_exported_functions_from(header, *functions)
8891
"yp_string_sizeof"
8992
)
9093

91-
# This object represents a yp_buffer_t. Its structure must be kept in sync
92-
# with the C version.
93-
class YPBuffer < FFI::Struct
94-
layout value: :pointer, length: :size_t, capacity: :size_t
94+
# This object represents a yp_buffer_t. We only use it as an opaque pointer,
95+
# so it doesn't need to know the fields of yp_buffer_t.
96+
class YPBuffer
97+
SIZEOF = LibRubyParser.yp_buffer_sizeof
9598

96-
# Read the contents of the buffer into a String object and return it.
97-
def to_ruby_string
98-
self[:value].read_string(self[:length])
99+
attr_reader :pointer
100+
101+
def initialize(pointer)
102+
@pointer = pointer
99103
end
100-
end
101104

102-
# Initialize a new buffer and yield it to the block. The buffer will be
103-
# automatically freed when the block returns.
104-
def self.with_buffer(&block)
105-
buffer = YPBuffer.new
106-
107-
begin
108-
raise unless yp_buffer_init(buffer)
109-
yield buffer
110-
ensure
111-
yp_buffer_free(buffer)
112-
buffer.pointer.free
105+
def value
106+
LibRubyParser.yp_buffer_value(pointer)
107+
end
108+
109+
def length
110+
LibRubyParser.yp_buffer_length(pointer)
111+
end
112+
113+
def read
114+
value.read_string(length)
115+
end
116+
117+
# Initialize a new buffer and yield it to the block. The buffer will be
118+
# automatically freed when the block returns.
119+
def self.with(&block)
120+
pointer = FFI::MemoryPointer.new(SIZEOF)
121+
122+
begin
123+
raise unless LibRubyParser.yp_buffer_init(pointer)
124+
yield new(pointer)
125+
ensure
126+
LibRubyParser.yp_buffer_free(pointer)
127+
pointer.free
128+
end
113129
end
114130
end
115131

116132
# This object represents a yp_string_t. We only use it as an opaque pointer,
117133
# so it doesn't have to be an FFI::Struct.
118134
class YPString
135+
SIZEOF = LibRubyParser.yp_string_sizeof
136+
119137
attr_reader :pointer
120138

121139
def initialize(pointer)
@@ -133,23 +151,18 @@ def length
133151
def read
134152
source.read_string(length)
135153
end
136-
end
137154

138-
# This is the size of a yp_string_t. It is returned by the yp_string_sizeof
139-
# function which we call once to ensure we have sufficient space for the
140-
# yp_string_t FFI pointer.
141-
SIZEOF_YP_STRING = yp_string_sizeof
142-
143-
# Yields a yp_string_t pointer to the given block.
144-
def self.with_string(filepath, &block)
145-
string = FFI::MemoryPointer.new(SIZEOF_YP_STRING)
146-
147-
begin
148-
raise unless yp_string_mapped_init(string, filepath)
149-
yield YPString.new(string)
150-
ensure
151-
yp_string_free(string)
152-
string.free
155+
# Yields a yp_string_t pointer to the given block.
156+
def self.with(filepath, &block)
157+
pointer = FFI::MemoryPointer.new(SIZEOF)
158+
159+
begin
160+
raise unless LibRubyParser.yp_string_mapped_init(pointer, filepath)
161+
yield new(pointer)
162+
ensure
163+
LibRubyParser.yp_string_free(pointer)
164+
pointer.free
165+
end
153166
end
154167
end
155168
end
@@ -162,10 +175,10 @@ def self.with_string(filepath, &block)
162175
VERSION = LibRubyParser.yp_version.read_string
163176

164177
def self.dump_internal(source, source_size, filepath)
165-
LibRubyParser.with_buffer do |buffer|
178+
LibRubyParser::YPBuffer.with do |buffer|
166179
metadata = [filepath.bytesize, filepath.b, 0].pack("LA*L") if filepath
167-
LibRubyParser.yp_parse_serialize(source, source_size, buffer, metadata)
168-
buffer.to_ruby_string
180+
LibRubyParser.yp_parse_serialize(source, source_size, buffer.pointer, metadata)
181+
buffer.read
169182
end
170183
end
171184
private_class_method :dump_internal
@@ -177,24 +190,24 @@ def self.dump(code, filepath = nil)
177190

178191
# Mirror the YARP.dump_file API by using the serialization API.
179192
def self.dump_file(filepath)
180-
LibRubyParser.with_string(filepath) do |string|
193+
LibRubyParser::YPString.with(filepath) do |string|
181194
dump_internal(string.source, string.length, filepath)
182195
end
183196
end
184197

185198
# Mirror the YARP.lex API by using the serialization API.
186199
def self.lex(code, filepath = nil)
187-
LibRubyParser.with_buffer do |buffer|
188-
LibRubyParser.yp_lex_serialize(code, code.bytesize, filepath, buffer)
189-
190-
source = Source.new(code)
191-
Serialize.load_tokens(source, buffer.to_ruby_string)
200+
LibRubyParser::YPBuffer.with do |buffer|
201+
LibRubyParser.yp_lex_serialize(code, code.bytesize, filepath, buffer.pointer)
202+
Serialize.load_tokens(Source.new(code), buffer.read)
192203
end
193204
end
194205

195206
# Mirror the YARP.lex_file API by using the serialization API.
196207
def self.lex_file(filepath)
197-
LibRubyParser.with_string(filepath) { |string| lex(string.read, filepath) }
208+
LibRubyParser::YPString.with(filepath) do |string|
209+
lex(string.read, filepath)
210+
end
198211
end
199212

200213
# Mirror the YARP.parse API by using the serialization API.
@@ -206,6 +219,8 @@ def self.parse(code, filepath = nil)
206219
# native strings instead of Ruby strings because it allows us to use mmap when
207220
# it is available.
208221
def self.parse_file(filepath)
209-
LibRubyParser.with_string(filepath) { |string| parse(string.read, filepath) }
222+
LibRubyParser::YPString.with(filepath) do |string|
223+
parse(string.read, filepath)
224+
end
210225
end
211226
end

src/util/yp_buffer.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
#define YP_BUFFER_INITIAL_SIZE 1024
44

5+
// Return the size of the yp_buffer_t struct.
6+
size_t
7+
yp_buffer_sizeof(void) {
8+
return sizeof(yp_buffer_t);
9+
}
10+
511
// Initialize a yp_buffer_t with its default values.
612
bool
713
yp_buffer_init(yp_buffer_t *buffer) {
@@ -12,6 +18,18 @@ yp_buffer_init(yp_buffer_t *buffer) {
1218
return buffer->value != NULL;
1319
}
1420

21+
// Return the value of the buffer.
22+
char *
23+
yp_buffer_value(yp_buffer_t *buffer) {
24+
return buffer->value;
25+
}
26+
27+
// Return the length of the buffer.
28+
size_t
29+
yp_buffer_length(yp_buffer_t *buffer) {
30+
return buffer->length;
31+
}
32+
1533
// Append the given amount of space to the buffer.
1634
static inline void
1735
yp_buffer_append_length(yp_buffer_t *buffer, size_t length) {

0 commit comments

Comments
 (0)