Ruby 3.3.0p0 (2023-12-25 revision 5124f9ac7513eb590c37717337c430cb93caa151)
api_pack.c
1#include "prism/extension.h"
2
3static VALUE rb_cPrism;
4static VALUE rb_cPrismPack;
5static VALUE rb_cPrismPackDirective;
6static VALUE rb_cPrismPackFormat;
7
8static VALUE v3_2_0_symbol;
9static VALUE pack_symbol;
10static VALUE unpack_symbol;
11
12#if SIZEOF_UINT64_T == SIZEOF_LONG_LONG
13# define UINT64T2NUM(x) ULL2NUM(x)
14# define NUM2UINT64T(x) (uint64_t)NUM2ULL(x)
15#elif SIZEOF_UINT64_T == SIZEOF_LONG
16# define UINT64T2NUM(x) ULONG2NUM(x)
17# define NUM2UINT64T(x) (uint64_t)NUM2ULONG(x)
18#else
19// error No uint64_t conversion
20#endif
21
22static VALUE
23pack_type_to_symbol(pm_pack_type type) {
24 switch (type) {
25 case PM_PACK_SPACE:
26 return ID2SYM(rb_intern("SPACE"));
27 case PM_PACK_COMMENT:
28 return ID2SYM(rb_intern("COMMENT"));
29 case PM_PACK_INTEGER:
30 return ID2SYM(rb_intern("INTEGER"));
31 case PM_PACK_UTF8:
32 return ID2SYM(rb_intern("UTF8"));
33 case PM_PACK_BER:
34 return ID2SYM(rb_intern("BER"));
35 case PM_PACK_FLOAT:
36 return ID2SYM(rb_intern("FLOAT"));
37 case PM_PACK_STRING_SPACE_PADDED:
38 return ID2SYM(rb_intern("STRING_SPACE_PADDED"));
39 case PM_PACK_STRING_NULL_PADDED:
40 return ID2SYM(rb_intern("STRING_NULL_PADDED"));
41 case PM_PACK_STRING_NULL_TERMINATED:
42 return ID2SYM(rb_intern("STRING_NULL_TERMINATED"));
43 case PM_PACK_STRING_MSB:
44 return ID2SYM(rb_intern("STRING_MSB"));
45 case PM_PACK_STRING_LSB:
46 return ID2SYM(rb_intern("STRING_LSB"));
47 case PM_PACK_STRING_HEX_HIGH:
48 return ID2SYM(rb_intern("STRING_HEX_HIGH"));
49 case PM_PACK_STRING_HEX_LOW:
50 return ID2SYM(rb_intern("STRING_HEX_LOW"));
51 case PM_PACK_STRING_UU:
52 return ID2SYM(rb_intern("STRING_UU"));
53 case PM_PACK_STRING_MIME:
54 return ID2SYM(rb_intern("STRING_MIME"));
55 case PM_PACK_STRING_BASE64:
56 return ID2SYM(rb_intern("STRING_BASE64"));
57 case PM_PACK_STRING_FIXED:
58 return ID2SYM(rb_intern("STRING_FIXED"));
59 case PM_PACK_STRING_POINTER:
60 return ID2SYM(rb_intern("STRING_POINTER"));
61 case PM_PACK_MOVE:
62 return ID2SYM(rb_intern("MOVE"));
63 case PM_PACK_BACK:
64 return ID2SYM(rb_intern("BACK"));
65 case PM_PACK_NULL:
66 return ID2SYM(rb_intern("NULL"));
67 default:
68 return Qnil;
69 }
70}
71
72static VALUE
73pack_signed_to_symbol(pm_pack_signed signed_type) {
74 switch (signed_type) {
75 case PM_PACK_UNSIGNED:
76 return ID2SYM(rb_intern("UNSIGNED"));
77 case PM_PACK_SIGNED:
78 return ID2SYM(rb_intern("SIGNED"));
79 case PM_PACK_SIGNED_NA:
80 return ID2SYM(rb_intern("SIGNED_NA"));
81 default:
82 return Qnil;
83 }
84}
85
86static VALUE
87pack_endian_to_symbol(pm_pack_endian endian) {
88 switch (endian) {
89 case PM_PACK_AGNOSTIC_ENDIAN:
90 return ID2SYM(rb_intern("AGNOSTIC_ENDIAN"));
91 case PM_PACK_LITTLE_ENDIAN:
92 return ID2SYM(rb_intern("LITTLE_ENDIAN"));
93 case PM_PACK_BIG_ENDIAN:
94 return ID2SYM(rb_intern("BIG_ENDIAN"));
95 case PM_PACK_NATIVE_ENDIAN:
96 return ID2SYM(rb_intern("NATIVE_ENDIAN"));
97 case PM_PACK_ENDIAN_NA:
98 return ID2SYM(rb_intern("ENDIAN_NA"));
99 default:
100 return Qnil;
101 }
102}
103
104static VALUE
105pack_size_to_symbol(pm_pack_size size) {
106 switch (size) {
107 case PM_PACK_SIZE_SHORT:
108 return ID2SYM(rb_intern("SIZE_SHORT"));
109 case PM_PACK_SIZE_INT:
110 return ID2SYM(rb_intern("SIZE_INT"));
111 case PM_PACK_SIZE_LONG:
112 return ID2SYM(rb_intern("SIZE_LONG"));
113 case PM_PACK_SIZE_LONG_LONG:
114 return ID2SYM(rb_intern("SIZE_LONG_LONG"));
115 case PM_PACK_SIZE_8:
116 return ID2SYM(rb_intern("SIZE_8"));
117 case PM_PACK_SIZE_16:
118 return ID2SYM(rb_intern("SIZE_16"));
119 case PM_PACK_SIZE_32:
120 return ID2SYM(rb_intern("SIZE_32"));
121 case PM_PACK_SIZE_64:
122 return ID2SYM(rb_intern("SIZE_64"));
123 case PM_PACK_SIZE_P:
124 return ID2SYM(rb_intern("SIZE_P"));
125 case PM_PACK_SIZE_NA:
126 return ID2SYM(rb_intern("SIZE_NA"));
127 default:
128 return Qnil;
129 }
130}
131
132static VALUE
133pack_length_type_to_symbol(pm_pack_length_type length_type) {
134 switch (length_type) {
135 case PM_PACK_LENGTH_FIXED:
136 return ID2SYM(rb_intern("LENGTH_FIXED"));
137 case PM_PACK_LENGTH_MAX:
138 return ID2SYM(rb_intern("LENGTH_MAX"));
139 case PM_PACK_LENGTH_RELATIVE:
140 return ID2SYM(rb_intern("LENGTH_RELATIVE"));
141 case PM_PACK_LENGTH_NA:
142 return ID2SYM(rb_intern("LENGTH_NA"));
143 default:
144 return Qnil;
145 }
146}
147
148static VALUE
149pack_encoding_to_ruby(pm_pack_encoding encoding) {
150 int index;
151 switch (encoding) {
152 case PM_PACK_ENCODING_ASCII_8BIT:
153 index = rb_ascii8bit_encindex();
154 break;
155 case PM_PACK_ENCODING_US_ASCII:
156 index = rb_usascii_encindex();
157 break;
158 case PM_PACK_ENCODING_UTF_8:
159 index = rb_utf8_encindex();
160 break;
161 default:
162 return Qnil;
163 }
164 return rb_enc_from_encoding(rb_enc_from_index(index));
165}
166
173static VALUE
174pack_parse(VALUE self, VALUE version_symbol, VALUE variant_symbol, VALUE format_string) {
175 if (version_symbol != v3_2_0_symbol) {
176 rb_raise(rb_eArgError, "invalid version");
177 }
178
179 pm_pack_variant variant;
180 if (variant_symbol == pack_symbol) {
181 variant = PM_PACK_VARIANT_PACK;
182 } else if (variant_symbol == unpack_symbol) {
183 variant = PM_PACK_VARIANT_UNPACK;
184 } else {
185 rb_raise(rb_eArgError, "invalid variant");
186 }
187
188 StringValue(format_string);
189
190 const char *format = RSTRING_PTR(format_string);
191 const char *format_end = format + RSTRING_LEN(format_string);
192 pm_pack_encoding encoding = PM_PACK_ENCODING_START;
193
194 VALUE directives_array = rb_ary_new();
195
196 while (format < format_end) {
198 pm_pack_signed signed_type;
199 pm_pack_endian endian;
200 pm_pack_size size;
201 pm_pack_length_type length_type;
202 uint64_t length;
203
204 const char *directive_start = format;
205
206 pm_pack_result parse_result = pm_pack_parse(variant, &format, format_end, &type, &signed_type, &endian,
207 &size, &length_type, &length, &encoding);
208
209 const char *directive_end = format;
210
211 switch (parse_result) {
212 case PM_PACK_OK:
213 break;
214 case PM_PACK_ERROR_UNSUPPORTED_DIRECTIVE:
215 rb_raise(rb_eArgError, "unsupported directive");
216 case PM_PACK_ERROR_UNKNOWN_DIRECTIVE:
217 rb_raise(rb_eArgError, "unsupported directive");
218 case PM_PACK_ERROR_LENGTH_TOO_BIG:
219 rb_raise(rb_eRangeError, "pack length too big");
220 case PM_PACK_ERROR_BANG_NOT_ALLOWED:
221 rb_raise(rb_eRangeError, "bang not allowed");
222 case PM_PACK_ERROR_DOUBLE_ENDIAN:
223 rb_raise(rb_eRangeError, "double endian");
224 default:
225 rb_bug("parse result");
226 }
227
228 if (type == PM_PACK_END) {
229 break;
230 }
231
232 VALUE directive_args[9] = {
233 version_symbol,
234 variant_symbol,
235 rb_usascii_str_new(directive_start, directive_end - directive_start),
236 pack_type_to_symbol(type),
237 pack_signed_to_symbol(signed_type),
238 pack_endian_to_symbol(endian),
239 pack_size_to_symbol(size),
240 pack_length_type_to_symbol(length_type),
241 UINT64T2NUM(length)
242 };
243
244 rb_ary_push(directives_array, rb_class_new_instance(9, directive_args, rb_cPrismPackDirective));
245 }
246
247 VALUE format_args[2];
248 format_args[0] = directives_array;
249 format_args[1] = pack_encoding_to_ruby(encoding);
250 return rb_class_new_instance(2, format_args, rb_cPrismPackFormat);
251}
252
256void
257Init_prism_pack(void) {
258 rb_cPrism = rb_define_module("Prism");
259 rb_cPrismPack = rb_define_module_under(rb_cPrism, "Pack");
260 rb_cPrismPackDirective = rb_define_class_under(rb_cPrismPack, "Directive", rb_cObject);
261 rb_cPrismPackFormat = rb_define_class_under(rb_cPrismPack, "Format", rb_cObject);
262 rb_define_singleton_method(rb_cPrismPack, "parse", pack_parse, 3);
263
264 v3_2_0_symbol = ID2SYM(rb_intern("v3_2_0"));
265 pack_symbol = ID2SYM(rb_intern("pack"));
266 unpack_symbol = ID2SYM(rb_intern("unpack"));
267}
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1002
VALUE rb_define_module(const char *name)
Defines a top-level module.
Definition class.c:1080
VALUE rb_define_module_under(VALUE outer, const char *name)
Defines a module under the namespace of outer.
Definition class.c:1104
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define Qnil
Old name of RUBY_Qnil.
VALUE rb_eRangeError
RangeError exception.
Definition error.c:1348
VALUE rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
Allocates, then initialises an instance of the given class.
Definition object.c:2090
#define rb_usascii_str_new(str, len)
Identical to rb_str_new, except it generates a string of "US ASCII" encoding.
Definition string.h:1532
VALUE type(ANYARGS)
ANYARGS-ed function type.
pm_pack_encoding
The type of encoding for a pack template string.
Definition pack.h:90
pm_pack_result
The result of parsing a pack template.
Definition pack.h:98
pm_pack_variant
The type of pack template we are parsing.
Definition pack.h:20
pm_pack_endian
The endianness of a pack directive.
Definition pack.h:59
pm_pack_signed
The signness of a pack directive.
Definition pack.h:52
pm_pack_size
The size of an integer pack directive.
Definition pack.h:68
pm_pack_length_type
The type of length of a pack directive.
Definition pack.h:82
pm_pack_type
A directive within the pack template.
Definition pack.h:26
#define StringValue(v)
Ensures that the parameter object is a String.
Definition rstring.h:66
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40