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