qr_reader-native 0.0.1
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.
- checksums.yaml +7 -0
- data/LICENSE-mruby.txt +22 -0
- data/LICENSE-spinel.txt +19 -0
- data/LICENSE.txt +21 -0
- data/README.md +61 -0
- data/ext/qr_reader_native/SPINEL_VERSION +1 -0
- data/ext/qr_reader_native/extconf.rb +8 -0
- data/ext/qr_reader_native/mruby/error.h +13 -0
- data/ext/qr_reader_native/mruby/internal.h +145 -0
- data/ext/qr_reader_native/mruby/string.h +22 -0
- data/ext/qr_reader_native/mruby.h +93 -0
- data/ext/qr_reader_native/mruby_shim.h +118 -0
- data/ext/qr_reader_native/qr_reader_native.c +13513 -0
- data/ext/qr_reader_native/qr_reader_native.h +29 -0
- data/ext/qr_reader_native/qr_reader_native_ext.c +598 -0
- data/ext/qr_reader_native/re_cased.h +71 -0
- data/ext/qr_reader_native/re_compile.c +5784 -0
- data/ext/qr_reader_native/re_ctype.h +3540 -0
- data/ext/qr_reader_native/re_exec.c +2190 -0
- data/ext/qr_reader_native/re_internal.h +671 -0
- data/ext/qr_reader_native/re_prop.h +4629 -0
- data/ext/qr_reader_native/re_spinel.c +363 -0
- data/ext/qr_reader_native/re_spinel.h +22 -0
- data/ext/qr_reader_native/re_spinel_utf8.inc +97 -0
- data/ext/qr_reader_native/re_utf8.c +156 -0
- data/ext/qr_reader_native/sp_alloc.c +1447 -0
- data/ext/qr_reader_native/sp_alloc.h +1196 -0
- data/ext/qr_reader_native/sp_argf.h +33 -0
- data/ext/qr_reader_native/sp_array.c +1052 -0
- data/ext/qr_reader_native/sp_array.h +456 -0
- data/ext/qr_reader_native/sp_bigint.c +5826 -0
- data/ext/qr_reader_native/sp_bigint.h +117 -0
- data/ext/qr_reader_native/sp_cold.c +5259 -0
- data/ext/qr_reader_native/sp_compat.h +402 -0
- data/ext/qr_reader_native/sp_core.c +818 -0
- data/ext/qr_reader_native/sp_core.h +56 -0
- data/ext/qr_reader_native/sp_crypto.c +877 -0
- data/ext/qr_reader_native/sp_crypto.h +169 -0
- data/ext/qr_reader_native/sp_dtoa.c +1533 -0
- data/ext/qr_reader_native/sp_dtoa.h +29 -0
- data/ext/qr_reader_native/sp_encoding_names.h +172 -0
- data/ext/qr_reader_native/sp_enum.h +86 -0
- data/ext/qr_reader_native/sp_exc.c +1185 -0
- data/ext/qr_reader_native/sp_exc.h +186 -0
- data/ext/qr_reader_native/sp_exc_ctx.h +56 -0
- data/ext/qr_reader_native/sp_fiber.c +889 -0
- data/ext/qr_reader_native/sp_fiber.h +162 -0
- data/ext/qr_reader_native/sp_fiber_ctx.h +84 -0
- data/ext/qr_reader_native/sp_format.c +695 -0
- data/ext/qr_reader_native/sp_format.h +83 -0
- data/ext/qr_reader_native/sp_gc.c +1793 -0
- data/ext/qr_reader_native/sp_gc.h +762 -0
- data/ext/qr_reader_native/sp_hash.c +131 -0
- data/ext/qr_reader_native/sp_hash.h +131 -0
- data/ext/qr_reader_native/sp_inspect.c +215 -0
- data/ext/qr_reader_native/sp_inspect.h +128 -0
- data/ext/qr_reader_native/sp_io.c +1676 -0
- data/ext/qr_reader_native/sp_io.h +272 -0
- data/ext/qr_reader_native/sp_iobuffer.c +1014 -0
- data/ext/qr_reader_native/sp_iobuffer.h +167 -0
- data/ext/qr_reader_native/sp_marshal.c +487 -0
- data/ext/qr_reader_native/sp_marshal.h +60 -0
- data/ext/qr_reader_native/sp_net.c +1029 -0
- data/ext/qr_reader_native/sp_net.h +205 -0
- data/ext/qr_reader_native/sp_pack.c +1254 -0
- data/ext/qr_reader_native/sp_poly_cold.c +1220 -0
- data/ext/qr_reader_native/sp_proc.c +410 -0
- data/ext/qr_reader_native/sp_proc.h +327 -0
- data/ext/qr_reader_native/sp_process.c +547 -0
- data/ext/qr_reader_native/sp_process_status.c +211 -0
- data/ext/qr_reader_native/sp_process_status.h +66 -0
- data/ext/qr_reader_native/sp_random.c +287 -0
- data/ext/qr_reader_native/sp_random.h +52 -0
- data/ext/qr_reader_native/sp_range.h +144 -0
- data/ext/qr_reader_native/sp_re.c +1379 -0
- data/ext/qr_reader_native/sp_re.h +186 -0
- data/ext/qr_reader_native/sp_re_names.h +59 -0
- data/ext/qr_reader_native/sp_sched.c +4044 -0
- data/ext/qr_reader_native/sp_sched.h +295 -0
- data/ext/qr_reader_native/sp_slab.c +1507 -0
- data/ext/qr_reader_native/sp_str.c +1462 -0
- data/ext/qr_reader_native/sp_str.h +306 -0
- data/ext/qr_reader_native/sp_str_crypt.c +23 -0
- data/ext/qr_reader_native/sp_string.c +92 -0
- data/ext/qr_reader_native/sp_string.h +253 -0
- data/ext/qr_reader_native/sp_system.c +78 -0
- data/ext/qr_reader_native/sp_system.h +18 -0
- data/ext/qr_reader_native/sp_time.c +860 -0
- data/ext/qr_reader_native/sp_time.h +100 -0
- data/ext/qr_reader_native/sp_types.h +292 -0
- data/ext/qr_reader_native/spinel_rt.h +19017 -0
- data/ext/qr_reader_native/unicase.c +399 -0
- data/ext/qr_reader_native/unicase.h +901 -0
- data/lib/qr_reader/native.rb +6 -0
- metadata +153 -0
|
@@ -0,0 +1,1014 @@
|
|
|
1
|
+
/* sp_iobuffer.c -- IO::Buffer, CRuby 4.x semantics (see sp_iobuffer.h for
|
|
2
|
+
the memory model). Bound through packages/io/io/buffer.rb; that file is
|
|
3
|
+
spliced into any program referencing `IO::Buffer`, so this object is
|
|
4
|
+
pulled from libspinel_rt.a only when the class is actually used.
|
|
5
|
+
|
|
6
|
+
Conversion edges follow CRuby's NUM2* family: 8/16-bit stores wrap any
|
|
7
|
+
64-bit value; 32-bit stores range-check ([-2^31, 2^32-1] unsigned,
|
|
8
|
+
[-2^31, 2^31-1] signed); 64-bit stores accept the full two's-complement
|
|
9
|
+
range plus Bignums up to the unsigned bound. Messages mirror CRuby's,
|
|
10
|
+
including the "integer ... to" (fixnum) / "bignum ... into" wording. */
|
|
11
|
+
#include "sp_iobuffer.h"
|
|
12
|
+
#include "sp_io.h" /* the IO integration: sp_File, the readiness parks */
|
|
13
|
+
#include <stdlib.h>
|
|
14
|
+
#include <string.h>
|
|
15
|
+
#include <stdio.h>
|
|
16
|
+
#include <math.h>
|
|
17
|
+
#include <unistd.h>
|
|
18
|
+
#include <errno.h>
|
|
19
|
+
#include <fcntl.h>
|
|
20
|
+
#include <sys/mman.h>
|
|
21
|
+
#include <sys/stat.h>
|
|
22
|
+
|
|
23
|
+
extern const char *sp_sprintf(const char *fmt, ...);
|
|
24
|
+
extern SP_NORETURN void sp_raise_cls(const char *cls, const char *msg);
|
|
25
|
+
|
|
26
|
+
/* sp_bigint.c helpers (sp_bigint.h is mruby-shim territory; the u64 pair is
|
|
27
|
+
added beside the other serialization helpers there) */
|
|
28
|
+
extern sp_Bigint *sp_bigint_new_u64(uint64_t v);
|
|
29
|
+
extern int sp_bigint_mag_u64(sp_Bigint *b, uint64_t *out); /* 1 iff |b| < 2^64 */
|
|
30
|
+
extern int sp_bigint_sign(sp_Bigint *b);
|
|
31
|
+
extern double sp_bigint_to_double(sp_Bigint *b);
|
|
32
|
+
|
|
33
|
+
/* ---- raising ---- */
|
|
34
|
+
|
|
35
|
+
static SP_NORETURN void iob_arg(const char *msg) { sp_raise_cls("ArgumentError", msg); }
|
|
36
|
+
|
|
37
|
+
/* The lock does NOT gate data access: reads and writes (and slice/dup/
|
|
38
|
+
copy/each/...) all run inside locked { }. Only ownership-shaped
|
|
39
|
+
operations refuse -- free / resize / transfer / a second locked { } --
|
|
40
|
+
each with its own message (matched against CRuby empirically; note the
|
|
41
|
+
lock also survives an exception out of the block). */
|
|
42
|
+
static void iob_readable(sp_IOBuffer *b) { (void)b; }
|
|
43
|
+
static void iob_writable(sp_IOBuffer *b) {
|
|
44
|
+
if (b->flags & SP_IOB_READONLY) sp_raise_cls("IO::Buffer::AccessError", "Buffer is not writable!");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* the value's Ruby spelling for TypeError messages (nil/true/false read as
|
|
48
|
+
themselves, like CRuby's) */
|
|
49
|
+
static const char *iob_val_name(sp_RbVal v) {
|
|
50
|
+
switch (v.tag) {
|
|
51
|
+
case SP_TAG_NIL: return "nil";
|
|
52
|
+
case SP_TAG_BOOL: return v.v.i ? "true" : "false";
|
|
53
|
+
case SP_TAG_INT: case SP_TAG_BIGINT: return "Integer";
|
|
54
|
+
case SP_TAG_FLT: return "Float";
|
|
55
|
+
case SP_TAG_STR: return "String";
|
|
56
|
+
case SP_TAG_SYM: return "Symbol";
|
|
57
|
+
case SP_TAG_CLASS: return "Class";
|
|
58
|
+
case SP_TAG_OBJ:
|
|
59
|
+
switch (v.cls_id) {
|
|
60
|
+
case SP_BUILTIN_INT_ARRAY: case SP_BUILTIN_STR_ARRAY:
|
|
61
|
+
case SP_BUILTIN_FLT_ARRAY: case SP_BUILTIN_PTR_ARRAY:
|
|
62
|
+
case SP_BUILTIN_SYM_ARRAY: case SP_BUILTIN_POLY_ARRAY:
|
|
63
|
+
return "Array";
|
|
64
|
+
case SP_BUILTIN_STR_INT_HASH: case SP_BUILTIN_STR_STR_HASH:
|
|
65
|
+
case SP_BUILTIN_INT_STR_HASH: case SP_BUILTIN_SYM_INT_HASH:
|
|
66
|
+
case SP_BUILTIN_SYM_STR_HASH: case SP_BUILTIN_STR_POLY_HASH:
|
|
67
|
+
case SP_BUILTIN_SYM_POLY_HASH: case SP_BUILTIN_POLY_POLY_HASH:
|
|
68
|
+
return "Hash";
|
|
69
|
+
case SP_BUILTIN_RANGE: return "Range";
|
|
70
|
+
case SP_BUILTIN_TIME: return "Time";
|
|
71
|
+
case SP_BUILTIN_PROC: return "Proc";
|
|
72
|
+
case SP_BUILTIN_IO: return "IO";
|
|
73
|
+
default: return "Object";
|
|
74
|
+
}
|
|
75
|
+
default: return "Object";
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* ---- base pointer + bounds ---- */
|
|
80
|
+
|
|
81
|
+
static uint8_t *iob_base(sp_IOBuffer *b) {
|
|
82
|
+
if (b->source) return b->source->data ? b->source->data + b->off : NULL;
|
|
83
|
+
return b->data;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* A slice whose root was freed, transferred, or shrunk below the view's
|
|
87
|
+
range must not be read through: CRuby raises InvalidatedError there, and
|
|
88
|
+
the stale base would be NULL or past the live allocation. */
|
|
89
|
+
static int iob_slice_invalid(sp_IOBuffer *b) {
|
|
90
|
+
return b->source && (!b->source->data || b->off > b->source->size - b->size);
|
|
91
|
+
}
|
|
92
|
+
static uint8_t *iob_ptr(sp_IOBuffer *b) {
|
|
93
|
+
if (iob_slice_invalid(b))
|
|
94
|
+
sp_raise_cls("IO::Buffer::InvalidatedError", "Buffer has been invalidated!");
|
|
95
|
+
return iob_base(b);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Overflow-safe: `off + len > size` wraps for large operands, so compare
|
|
99
|
+
against the remaining room instead (both are non-negative here). */
|
|
100
|
+
static void iob_range(sp_IOBuffer *b, int64_t off, int64_t len) {
|
|
101
|
+
if (off < 0) iob_arg("Offset can't be negative!");
|
|
102
|
+
if (len < 0) iob_arg("Length can't be negative!");
|
|
103
|
+
if (off > b->size || len > b->size - off) iob_arg("Specified offset+length is bigger than the buffer size!");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* ---- the type table ---- */
|
|
107
|
+
|
|
108
|
+
typedef struct { const char *name; int8_t width; int8_t be; int8_t sign; int8_t flt; } IobType;
|
|
109
|
+
static const IobType iob_types[SP_IOB_TY__COUNT] = {
|
|
110
|
+
[SP_IOB_TY_U8] = {"U8", 1, 0, 0, 0}, [SP_IOB_TY_S8] = {"S8", 1, 0, 1, 0},
|
|
111
|
+
[SP_IOB_TY_u16] = {"u16", 2, 0, 0, 0}, [SP_IOB_TY_s16] = {"s16", 2, 0, 1, 0},
|
|
112
|
+
[SP_IOB_TY_U16] = {"U16", 2, 1, 0, 0}, [SP_IOB_TY_S16] = {"S16", 2, 1, 1, 0},
|
|
113
|
+
[SP_IOB_TY_u32] = {"u32", 4, 0, 0, 0}, [SP_IOB_TY_s32] = {"s32", 4, 0, 1, 0},
|
|
114
|
+
[SP_IOB_TY_U32] = {"U32", 4, 1, 0, 0}, [SP_IOB_TY_S32] = {"S32", 4, 1, 1, 0},
|
|
115
|
+
[SP_IOB_TY_u64] = {"u64", 8, 0, 0, 0}, [SP_IOB_TY_s64] = {"s64", 8, 0, 1, 0},
|
|
116
|
+
[SP_IOB_TY_U64] = {"U64", 8, 1, 0, 0}, [SP_IOB_TY_S64] = {"S64", 8, 1, 1, 0},
|
|
117
|
+
[SP_IOB_TY_f32] = {"f32", 4, 0, 1, 1}, [SP_IOB_TY_f64] = {"f64", 8, 0, 1, 1},
|
|
118
|
+
[SP_IOB_TY_F32] = {"F32", 4, 1, 1, 1}, [SP_IOB_TY_F64] = {"F64", 8, 1, 1, 1},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
int sp_IOBuffer_type_of(const char *name) {
|
|
122
|
+
if (!name) return -1;
|
|
123
|
+
for (int i = 0; i < SP_IOB_TY__COUNT; i++)
|
|
124
|
+
if (strcmp(iob_types[i].name, name) == 0) return i;
|
|
125
|
+
return -1;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
static int iob_decode_type(sp_RbVal type) {
|
|
129
|
+
if (type.tag == SP_TAG_SYM && sp_sym_name_fn) {
|
|
130
|
+
int t = sp_IOBuffer_type_of(sp_sym_name_fn((sp_sym)type.v.i));
|
|
131
|
+
if (t >= 0) return t;
|
|
132
|
+
}
|
|
133
|
+
iob_arg("Invalid type name!");
|
|
134
|
+
return -1;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static uint8_t *iob_value_ptr(sp_IOBuffer *b, int64_t off, int width) {
|
|
138
|
+
if (off < 0) iob_arg("Offset can't be negative!");
|
|
139
|
+
if (b->size < width || off > b->size - width)
|
|
140
|
+
iob_arg(sp_sprintf("Type extends beyond end of buffer! (offset=%lld > size=%lld)",
|
|
141
|
+
(long long)off, (long long)b->size));
|
|
142
|
+
return iob_ptr(b) + off;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* ---- raw loads/stores (byte-assembled: host-endian agnostic) ---- */
|
|
146
|
+
|
|
147
|
+
static uint64_t iob_load(const uint8_t *p, int width, int be) {
|
|
148
|
+
uint64_t v = 0;
|
|
149
|
+
if (be) for (int i = 0; i < width; i++) v = (v << 8) | p[i];
|
|
150
|
+
else for (int i = 0; i < width; i++) v |= (uint64_t)p[i] << (8 * i);
|
|
151
|
+
return v;
|
|
152
|
+
}
|
|
153
|
+
static void iob_store(uint8_t *p, int width, int be, uint64_t v) {
|
|
154
|
+
if (be) for (int i = 0; i < width; i++) p[i] = (uint8_t)(v >> (8 * (width - 1 - i)));
|
|
155
|
+
else for (int i = 0; i < width; i++) p[i] = (uint8_t)(v >> (8 * i));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* ---- value conversion (CRuby NUM2* semantics + messages) ---- */
|
|
159
|
+
|
|
160
|
+
static int64_t iob_float_to_i64(double f) {
|
|
161
|
+
if (isnan(f)) sp_raise_cls("FloatDomainError", "NaN");
|
|
162
|
+
if (isinf(f)) sp_raise_cls("FloatDomainError", f > 0 ? "Infinity" : "-Infinity");
|
|
163
|
+
f = trunc(f);
|
|
164
|
+
if (f < -9223372036854775808.0 || f >= 9223372036854775808.0)
|
|
165
|
+
sp_raise_cls("RangeError", "float out of range of integer");
|
|
166
|
+
return (int64_t)f;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* Convert `val` to the store word for integer type `ty`. */
|
|
170
|
+
static uint64_t iob_int_operand(int ty, sp_RbVal val) {
|
|
171
|
+
const IobType *t = &iob_types[ty];
|
|
172
|
+
const char *cname =
|
|
173
|
+
t->width == 8 ? (t->sign ? "long long" : "unsigned long long")
|
|
174
|
+
: t->width == 4 ? (t->sign ? "int" : "unsigned int")
|
|
175
|
+
: (t->sign ? "long" : "unsigned long");
|
|
176
|
+
int64_t iv;
|
|
177
|
+
if (val.tag == SP_TAG_INT) iv = val.v.i;
|
|
178
|
+
else if (val.tag == SP_TAG_FLT) iv = iob_float_to_i64(val.v.f);
|
|
179
|
+
else if (val.tag == SP_TAG_BIGINT) {
|
|
180
|
+
sp_Bigint *bg = (sp_Bigint *)val.v.p;
|
|
181
|
+
uint64_t mag;
|
|
182
|
+
int fits = sp_bigint_mag_u64(bg, &mag);
|
|
183
|
+
int neg = sp_bigint_sign(bg) < 0;
|
|
184
|
+
if (t->width == 8 && !t->sign) {
|
|
185
|
+
/* u64: [-2^63, 2^64-1] */
|
|
186
|
+
if (!neg) {
|
|
187
|
+
if (!fits) sp_raise_cls("RangeError", sp_sprintf("bignum too big to convert into '%s'", cname));
|
|
188
|
+
return mag;
|
|
189
|
+
}
|
|
190
|
+
if (!fits || mag > (uint64_t)1 << 63)
|
|
191
|
+
sp_raise_cls("RangeError", sp_sprintf("bignum out of range of %s", cname));
|
|
192
|
+
return (uint64_t)0 - mag;
|
|
193
|
+
}
|
|
194
|
+
if (t->width <= 2) {
|
|
195
|
+
/* 8/16-bit: any value representable in 64 bits wraps */
|
|
196
|
+
if (!neg) {
|
|
197
|
+
if (!fits) sp_raise_cls("RangeError", sp_sprintf("bignum too big to convert into '%s'", cname));
|
|
198
|
+
return mag;
|
|
199
|
+
}
|
|
200
|
+
if (!fits || mag > (uint64_t)1 << 63)
|
|
201
|
+
sp_raise_cls("RangeError", sp_sprintf("bignum too small to convert into '%s'", cname));
|
|
202
|
+
return (uint64_t)0 - mag;
|
|
203
|
+
}
|
|
204
|
+
/* s64: a Bignum is out of range except at the negative bound (any heap
|
|
205
|
+
Bignum a 64-bit build makes has |v| >= 2^63; smaller ones stay sp_int). */
|
|
206
|
+
if (t->width == 8 && t->sign) {
|
|
207
|
+
if (fits && !neg && mag <= (uint64_t)INT64_MAX) return mag;
|
|
208
|
+
if (fits && neg && mag <= (uint64_t)1 << 63) return (uint64_t)0 - mag;
|
|
209
|
+
}
|
|
210
|
+
/* 32-bit: the type's own width decides, not how the value is held. On a
|
|
211
|
+
32-bit build sp_int is 32 bits wide and every value past 2^31-1 is a
|
|
212
|
+
heap Bignum, 0xCAFEBABE included, which is most of the range a U32 is
|
|
213
|
+
asked for (#4647). A magnitude that fits the field is stored. */
|
|
214
|
+
if (t->width == 4 && fits) {
|
|
215
|
+
uint64_t hi = t->sign ? ((uint64_t)1 << 31) - 1 : ((uint64_t)1 << 32) - 1;
|
|
216
|
+
if (!neg && mag <= hi) return mag;
|
|
217
|
+
if (neg && mag <= (uint64_t)1 << 31) return (uint64_t)0 - mag;
|
|
218
|
+
}
|
|
219
|
+
sp_raise_cls("RangeError", sp_sprintf(neg ? "bignum too small to convert into '%s'"
|
|
220
|
+
: "bignum too big to convert into '%s'", cname));
|
|
221
|
+
return 0;
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
sp_raise_cls("TypeError", sp_sprintf("no implicit conversion of %s into Integer", iob_val_name(val)));
|
|
225
|
+
return 0;
|
|
226
|
+
}
|
|
227
|
+
if (t->width == 4) {
|
|
228
|
+
int64_t lo = -((int64_t)1 << 31);
|
|
229
|
+
int64_t hi = t->sign ? ((int64_t)1 << 31) - 1 : ((int64_t)1 << 32) - 1;
|
|
230
|
+
if (iv < lo) sp_raise_cls("RangeError", sp_sprintf("integer %lld too small to convert to '%s'", (long long)iv, cname));
|
|
231
|
+
if (iv > hi) sp_raise_cls("RangeError", sp_sprintf("integer %lld too big to convert to '%s'", (long long)iv, cname));
|
|
232
|
+
}
|
|
233
|
+
return (uint64_t)iv;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
static double iob_float_operand(sp_RbVal val) {
|
|
237
|
+
if (val.tag == SP_TAG_FLT) return val.v.f;
|
|
238
|
+
if (val.tag == SP_TAG_INT) return (double)val.v.i;
|
|
239
|
+
if (val.tag == SP_TAG_BIGINT) return sp_bigint_to_double((sp_Bigint *)val.v.p);
|
|
240
|
+
if (val.tag == SP_TAG_OBJ && val.cls_id == SP_BUILTIN_RATIONAL && val.v.p) {
|
|
241
|
+
sp_Rational *r = (sp_Rational *)val.v.p;
|
|
242
|
+
return (double)r->num / (double)r->den;
|
|
243
|
+
}
|
|
244
|
+
sp_raise_cls("TypeError", sp_sprintf("can't convert %s into Float", iob_val_name(val)));
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* ---- boxed load/store cores shared by the generic and typed entries ---- */
|
|
249
|
+
|
|
250
|
+
static sp_RbVal iob_get_core(sp_IOBuffer *b, int ty, int64_t off) {
|
|
251
|
+
const IobType *t = &iob_types[ty];
|
|
252
|
+
iob_readable(b);
|
|
253
|
+
const uint8_t *p = iob_value_ptr(b, off, t->width);
|
|
254
|
+
if (t->flt) {
|
|
255
|
+
uint64_t raw = iob_load(p, t->width, t->be);
|
|
256
|
+
if (t->width == 4) { union { uint32_t u; float f; } u; u.u = (uint32_t)raw; return sp_box_float(u.f); }
|
|
257
|
+
union { uint64_t u; double f; } u; u.u = raw; return sp_box_float(u.f);
|
|
258
|
+
}
|
|
259
|
+
uint64_t raw = iob_load(p, t->width, t->be);
|
|
260
|
+
/* A loaded value goes out as a machine int when it fits sp_int and is not
|
|
261
|
+
SP_INT_NIL (the nullable-int sentinel, INTPTR_MIN), and as a Bignum
|
|
262
|
+
otherwise. That is by the WIDTH OF sp_int, not of the field: on a 32-bit
|
|
263
|
+
build a U32 above 2**31-1 and an S32 of exactly INT32_MIN are Bignums
|
|
264
|
+
the same way a u64 above 2**63-1 and an s64 of INT64_MIN are on a 64-bit
|
|
265
|
+
one (#4647). */
|
|
266
|
+
if (t->sign) {
|
|
267
|
+
/* sign-extend from width */
|
|
268
|
+
int shift = 64 - 8 * t->width;
|
|
269
|
+
int64_t sv = (int64_t)(raw << shift) >> shift;
|
|
270
|
+
if (sv < (int64_t)INTPTR_MIN || sv > (int64_t)INTPTR_MAX || (sp_int)sv == SP_INT_NIL) {
|
|
271
|
+
SP_GC_ROOT(b);
|
|
272
|
+
return sp_box_bigint(sp_bigint_new_int(sv));
|
|
273
|
+
}
|
|
274
|
+
return sp_box_int((sp_int)sv);
|
|
275
|
+
}
|
|
276
|
+
if (raw > (uint64_t)INTPTR_MAX) {
|
|
277
|
+
SP_GC_ROOT(b);
|
|
278
|
+
return sp_box_bigint(sp_bigint_new_u64(raw));
|
|
279
|
+
}
|
|
280
|
+
return sp_box_int((sp_int)raw);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
static sp_int iob_set_core(sp_IOBuffer *b, int ty, int64_t off, sp_RbVal val) {
|
|
284
|
+
const IobType *t = &iob_types[ty];
|
|
285
|
+
iob_writable(b);
|
|
286
|
+
uint8_t *p = iob_value_ptr(b, off, t->width);
|
|
287
|
+
if (t->flt) {
|
|
288
|
+
double f = iob_float_operand(val);
|
|
289
|
+
uint64_t raw;
|
|
290
|
+
if (t->width == 4) { union { uint32_t u; float f; } u; u.f = (float)f; raw = u.u; }
|
|
291
|
+
else { union { uint64_t u; double f; } u; u.f = f; raw = u.u; }
|
|
292
|
+
iob_store(p, t->width, t->be, raw);
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
iob_store(p, t->width, t->be, iob_int_operand(ty, val));
|
|
296
|
+
}
|
|
297
|
+
return t->width;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* ---- constructors ---- */
|
|
301
|
+
|
|
302
|
+
static void iob_scan(void *p) {
|
|
303
|
+
sp_IOBuffer *b = (sp_IOBuffer *)p;
|
|
304
|
+
if (b->source) sp_gc_mark(b->source);
|
|
305
|
+
}
|
|
306
|
+
/* Give the allocation back: munmap for a mapped file, free otherwise. */
|
|
307
|
+
static void iob_release(sp_IOBuffer *b) {
|
|
308
|
+
if (b->map_base) { munmap(b->map_base, b->map_len); b->map_base = NULL; b->map_len = 0; }
|
|
309
|
+
else free(b->data);
|
|
310
|
+
b->data = NULL;
|
|
311
|
+
}
|
|
312
|
+
void sp_IOBuffer_fin(void *p) {
|
|
313
|
+
iob_release((sp_IOBuffer *)p);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
static sp_IOBuffer *iob_alloc_obj(sp_int cls_id) {
|
|
317
|
+
sp_IOBuffer *b = (sp_IOBuffer *)sp_gc_alloc(sizeof(sp_IOBuffer), sp_IOBuffer_fin, iob_scan);
|
|
318
|
+
memset(b, 0, sizeof *b);
|
|
319
|
+
b->cls_id = cls_id;
|
|
320
|
+
return b;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
sp_int sp_IOBuffer_page_size(void) {
|
|
324
|
+
long ps = sysconf(_SC_PAGESIZE);
|
|
325
|
+
return ps > 0 ? (sp_int)ps : 4096;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
static void iob_allocate(sp_IOBuffer *b, int64_t size, uint32_t flags, int flags_given) {
|
|
329
|
+
if (size < 0) iob_arg("Size can't be negative!");
|
|
330
|
+
if (!flags_given)
|
|
331
|
+
flags = size == 0 ? 0
|
|
332
|
+
: size >= (int64_t)sp_IOBuffer_page_size() ? SP_IOB_MAPPED : SP_IOB_INTERNAL;
|
|
333
|
+
else if (size > 0 && !(flags & (SP_IOB_INTERNAL | SP_IOB_MAPPED)))
|
|
334
|
+
sp_raise_cls("IO::Buffer::AllocationError", "Could not allocate buffer!");
|
|
335
|
+
b->flags = flags;
|
|
336
|
+
b->size = size;
|
|
337
|
+
if (size > 0) {
|
|
338
|
+
b->data = (uint8_t *)calloc(1, (size_t)size);
|
|
339
|
+
if (!b->data) sp_oom_die();
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
sp_IOBuffer *sp_IOBuffer_new(sp_int cls_id) {
|
|
344
|
+
sp_IOBuffer *b = iob_alloc_obj(cls_id);
|
|
345
|
+
iob_allocate(b, 65536, 0, 0);
|
|
346
|
+
return b;
|
|
347
|
+
}
|
|
348
|
+
sp_IOBuffer *sp_IOBuffer_new_i(sp_int cls_id, sp_int size) {
|
|
349
|
+
sp_IOBuffer *b = iob_alloc_obj(cls_id);
|
|
350
|
+
iob_allocate(b, size, 0, 0);
|
|
351
|
+
return b;
|
|
352
|
+
}
|
|
353
|
+
sp_IOBuffer *sp_IOBuffer_new_if(sp_int cls_id, sp_int size, sp_int flags) {
|
|
354
|
+
sp_IOBuffer *b = iob_alloc_obj(cls_id);
|
|
355
|
+
iob_allocate(b, size, (uint32_t)flags, 1);
|
|
356
|
+
return b;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
sp_IOBuffer *sp_IOBuffer_become_for(sp_IOBuffer *b, const char *s) {
|
|
360
|
+
size_t n = sp_str_byte_len(s);
|
|
361
|
+
uint8_t *d = (uint8_t *)malloc(n ? n : 1); /* non-NULL even for "": for("") is not a null buffer */
|
|
362
|
+
if (!d) sp_oom_die();
|
|
363
|
+
memcpy(d, s, n);
|
|
364
|
+
iob_release(b);
|
|
365
|
+
b->data = d;
|
|
366
|
+
b->source = NULL;
|
|
367
|
+
b->off = 0;
|
|
368
|
+
b->size = (int64_t)n;
|
|
369
|
+
b->flags = SP_IOB_EXTERNAL | SP_IOB_READONLY | SP_IOB_SLICE;
|
|
370
|
+
return b;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/* ---- generic + typed accessors ---- */
|
|
374
|
+
|
|
375
|
+
sp_RbVal sp_IOBuffer_get_value(sp_IOBuffer *b, sp_RbVal type, sp_int off) {
|
|
376
|
+
return iob_get_core(b, iob_decode_type(type), off);
|
|
377
|
+
}
|
|
378
|
+
sp_int sp_IOBuffer_set_value(sp_IOBuffer *b, sp_RbVal type, sp_int off, sp_RbVal val) {
|
|
379
|
+
return iob_set_core(b, iob_decode_type(type), off, val);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
sp_int sp_IOBuffer_get_i(sp_IOBuffer *b, sp_int ty, sp_int off) {
|
|
383
|
+
const IobType *t = &iob_types[ty];
|
|
384
|
+
iob_readable(b);
|
|
385
|
+
const uint8_t *p = iob_value_ptr(b, off, t->width);
|
|
386
|
+
uint64_t raw = iob_load(p, t->width, t->be);
|
|
387
|
+
if (t->sign) { int shift = 64 - 8 * t->width; return (int64_t)(raw << shift) >> shift; }
|
|
388
|
+
return (sp_int)raw;
|
|
389
|
+
}
|
|
390
|
+
sp_RbVal sp_IOBuffer_get_x(sp_IOBuffer *b, sp_int ty, sp_int off) {
|
|
391
|
+
return iob_get_core(b, (int)ty, off);
|
|
392
|
+
}
|
|
393
|
+
double sp_IOBuffer_get_f(sp_IOBuffer *b, sp_int ty, sp_int off) {
|
|
394
|
+
const IobType *t = &iob_types[ty];
|
|
395
|
+
iob_readable(b);
|
|
396
|
+
const uint8_t *p = iob_value_ptr(b, off, t->width);
|
|
397
|
+
uint64_t raw = iob_load(p, t->width, t->be);
|
|
398
|
+
if (t->width == 4) { union { uint32_t u; float f; } u; u.u = (uint32_t)raw; return u.f; }
|
|
399
|
+
union { uint64_t u; double f; } u; u.u = raw; return u.f;
|
|
400
|
+
}
|
|
401
|
+
sp_int sp_IOBuffer_set_i(sp_IOBuffer *b, sp_int ty, sp_int off, sp_int v) {
|
|
402
|
+
return iob_set_core(b, ty, off, sp_box_int(v));
|
|
403
|
+
}
|
|
404
|
+
sp_int sp_IOBuffer_set_f(sp_IOBuffer *b, sp_int ty, sp_int off, double v) {
|
|
405
|
+
return iob_set_core(b, ty, off, sp_box_float(v));
|
|
406
|
+
}
|
|
407
|
+
/* The typed lowering with a BOXED value: the generic path minus the symbol
|
|
408
|
+
decode. A boxed value cannot go through set_i -- an integer type takes a
|
|
409
|
+
Float (truncating, as CRuby does) and u64/s64 take a Bignum -- so it
|
|
410
|
+
reaches iob_set_core unexamined, exactly as set_value leaves it. */
|
|
411
|
+
sp_int sp_IOBuffer_set_v(sp_IOBuffer *b, sp_int ty, sp_int off, sp_RbVal v) {
|
|
412
|
+
return iob_set_core(b, ty, off, v);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/* ---- strings ---- */
|
|
416
|
+
|
|
417
|
+
/* The bare-offset arm gets its own message only when the LENGTH was
|
|
418
|
+
defaulted (CRuby: get_string(9) names the offset, get_string(9, 0) the
|
|
419
|
+
offset+length). */
|
|
420
|
+
static const char *iob_get_string(sp_IOBuffer *b, int64_t off, int64_t len, int len_given) {
|
|
421
|
+
SP_GC_ROOT(b);
|
|
422
|
+
iob_readable(b);
|
|
423
|
+
if (off < 0) iob_arg("Offset can't be negative!");
|
|
424
|
+
if (!len_given) {
|
|
425
|
+
if (off > b->size) iob_arg("The given offset is bigger than the buffer size!");
|
|
426
|
+
len = b->size - off;
|
|
427
|
+
}
|
|
428
|
+
if (len < 0) iob_arg("Length can't be negative!");
|
|
429
|
+
if (off > b->size || len > b->size - off) iob_arg("Specified offset+length is bigger than the buffer size!");
|
|
430
|
+
char *r = sp_str_alloc_raw((size_t)len + 1);
|
|
431
|
+
if (len > 0) memcpy(r, iob_ptr(b) + off, (size_t)len);
|
|
432
|
+
r[len] = '\0';
|
|
433
|
+
sp_str_set_len(r, (size_t)len);
|
|
434
|
+
sp_str_mark_binary(r);
|
|
435
|
+
return r;
|
|
436
|
+
}
|
|
437
|
+
const char *sp_IOBuffer_get_string0(sp_IOBuffer *b) { return iob_get_string(b, 0, 0, 0); }
|
|
438
|
+
const char *sp_IOBuffer_get_string1(sp_IOBuffer *b, sp_int off) { return iob_get_string(b, off, 0, 0); }
|
|
439
|
+
const char *sp_IOBuffer_get_string2(sp_IOBuffer *b, sp_int off, sp_int len) { return iob_get_string(b, off, len, 1); }
|
|
440
|
+
|
|
441
|
+
static sp_int iob_set_string(sp_IOBuffer *b, const char *s, int64_t off, int64_t len, int64_t soff, int len_given) {
|
|
442
|
+
iob_writable(b);
|
|
443
|
+
int64_t slen = (int64_t)sp_str_byte_len(s);
|
|
444
|
+
if (off < 0) iob_arg("Offset can't be negative!");
|
|
445
|
+
if (soff < 0) iob_arg("Source offset can't be negative!");
|
|
446
|
+
if (soff > slen) iob_arg("The given source offset is bigger than the source itself!");
|
|
447
|
+
if (!len_given) len = slen - soff;
|
|
448
|
+
if (len < 0) iob_arg("Length can't be negative!");
|
|
449
|
+
/* destination range before source range, as CRuby orders them */
|
|
450
|
+
if (off > b->size || len > b->size - off) iob_arg("Specified offset+length is bigger than the buffer size!");
|
|
451
|
+
if (len > slen - soff) iob_arg("The computed source range exceeds the size of the source buffer!");
|
|
452
|
+
if (len > 0) memmove(iob_ptr(b) + off, s + soff, (size_t)len);
|
|
453
|
+
return len;
|
|
454
|
+
}
|
|
455
|
+
sp_int sp_IOBuffer_set_string1(sp_IOBuffer *b, const char *s) { return iob_set_string(b, s, 0, 0, 0, 0); }
|
|
456
|
+
sp_int sp_IOBuffer_set_string2(sp_IOBuffer *b, const char *s, sp_int off) { return iob_set_string(b, s, off, 0, 0, 0); }
|
|
457
|
+
sp_int sp_IOBuffer_set_string3(sp_IOBuffer *b, const char *s, sp_int off, sp_int len) { return iob_set_string(b, s, off, len, 0, 1); }
|
|
458
|
+
sp_int sp_IOBuffer_set_string4(sp_IOBuffer *b, const char *s, sp_int off, sp_int len, sp_int soff) { return iob_set_string(b, s, off, len, soff, 1); }
|
|
459
|
+
|
|
460
|
+
/* ---- whole-buffer operations ---- */
|
|
461
|
+
|
|
462
|
+
sp_int sp_IOBuffer_size(sp_IOBuffer *b) { return b->size; }
|
|
463
|
+
|
|
464
|
+
sp_IOBuffer *sp_IOBuffer_resize(sp_IOBuffer *b, sp_int size) {
|
|
465
|
+
if (b->flags & SP_IOB_LOCKED)
|
|
466
|
+
sp_raise_cls("IO::Buffer::LockedError", "Cannot resize locked buffer!");
|
|
467
|
+
if (b->flags & SP_IOB_EXTERNAL) sp_raise_cls("IO::Buffer::AccessError", "Cannot resize external buffer!");
|
|
468
|
+
/* a file mapping has the file's length; growing it would detach the
|
|
469
|
+
view from the file it was made for, so it is refused as EXTERNAL is */
|
|
470
|
+
if (b->map_base) sp_raise_cls("IO::Buffer::AccessError", "Cannot resize mapped file buffer!");
|
|
471
|
+
if (size < 0) iob_arg("Size can't be negative!");
|
|
472
|
+
if (b->source) {
|
|
473
|
+
/* a slice detaches into its own (internal) allocation */
|
|
474
|
+
uint8_t *base = iob_ptr(b);
|
|
475
|
+
uint8_t *d = NULL;
|
|
476
|
+
if (size > 0) {
|
|
477
|
+
d = (uint8_t *)calloc(1, (size_t)size);
|
|
478
|
+
if (!d) sp_oom_die();
|
|
479
|
+
int64_t keep = b->size < size ? b->size : size;
|
|
480
|
+
if (base && keep > 0) memcpy(d, base, (size_t)keep);
|
|
481
|
+
}
|
|
482
|
+
b->source = NULL;
|
|
483
|
+
b->off = 0;
|
|
484
|
+
b->data = d;
|
|
485
|
+
b->size = size;
|
|
486
|
+
b->flags = SP_IOB_INTERNAL | (b->flags & SP_IOB_READONLY);
|
|
487
|
+
return b;
|
|
488
|
+
}
|
|
489
|
+
if (size == 0) {
|
|
490
|
+
free(b->data);
|
|
491
|
+
b->data = NULL;
|
|
492
|
+
b->size = 0;
|
|
493
|
+
return b;
|
|
494
|
+
}
|
|
495
|
+
uint8_t *d = (uint8_t *)realloc(b->data, (size_t)size);
|
|
496
|
+
if (!d) sp_oom_die();
|
|
497
|
+
if (size > b->size) memset(d + b->size, 0, (size_t)(size - b->size));
|
|
498
|
+
b->data = d;
|
|
499
|
+
b->size = size;
|
|
500
|
+
if (!(b->flags & (SP_IOB_INTERNAL | SP_IOB_MAPPED))) b->flags |= SP_IOB_INTERNAL;
|
|
501
|
+
return b;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
static sp_IOBuffer *iob_clear(sp_IOBuffer *b, int64_t v, int64_t off, int64_t len) {
|
|
505
|
+
iob_writable(b);
|
|
506
|
+
iob_range(b, off, len);
|
|
507
|
+
if (len > 0) memset(iob_ptr(b) + off, (int)(uint8_t)v, (size_t)len);
|
|
508
|
+
return b;
|
|
509
|
+
}
|
|
510
|
+
sp_IOBuffer *sp_IOBuffer_clear0(sp_IOBuffer *b) { return iob_clear(b, 0, 0, b->size); }
|
|
511
|
+
sp_IOBuffer *sp_IOBuffer_clear1(sp_IOBuffer *b, sp_int v) { return iob_clear(b, v, 0, b->size); }
|
|
512
|
+
sp_IOBuffer *sp_IOBuffer_clear2(sp_IOBuffer *b, sp_int v, sp_int off) {
|
|
513
|
+
return iob_clear(b, v, off, off <= b->size ? b->size - off : 0);
|
|
514
|
+
}
|
|
515
|
+
sp_IOBuffer *sp_IOBuffer_clear3(sp_IOBuffer *b, sp_int v, sp_int off, sp_int len) { return iob_clear(b, v, off, len); }
|
|
516
|
+
|
|
517
|
+
static sp_IOBuffer *iob_other(sp_IOBuffer *b, sp_RbVal v) {
|
|
518
|
+
if (v.tag == SP_TAG_OBJ && v.v.p && v.cls_id == (int)b->cls_id) return (sp_IOBuffer *)v.v.p;
|
|
519
|
+
sp_raise_cls("TypeError", sp_sprintf("wrong argument type %s (expected IO::Buffer)", iob_val_name(v)));
|
|
520
|
+
return NULL;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
static sp_int iob_copy(sp_IOBuffer *b, sp_RbVal srcv, int64_t off, int64_t len, int64_t soff, int len_given) {
|
|
524
|
+
sp_IOBuffer *src = iob_other(b, srcv);
|
|
525
|
+
iob_writable(b);
|
|
526
|
+
iob_readable(src);
|
|
527
|
+
if (off < 0) iob_arg("Offset can't be negative!");
|
|
528
|
+
if (soff < 0) iob_arg("Source offset can't be negative!");
|
|
529
|
+
if (soff > src->size) iob_arg("The given source offset is bigger than the source itself!");
|
|
530
|
+
if (!len_given) len = src->size - soff;
|
|
531
|
+
if (len < 0) iob_arg("Length can't be negative!");
|
|
532
|
+
/* destination range before source range, as CRuby orders them */
|
|
533
|
+
if (off > b->size || len > b->size - off) iob_arg("Specified offset+length is bigger than the buffer size!");
|
|
534
|
+
if (len > src->size - soff)
|
|
535
|
+
iob_arg("The computed source range exceeds the size of the source buffer!");
|
|
536
|
+
if (len > 0) memmove(iob_ptr(b) + off, iob_ptr(src) + soff, (size_t)len);
|
|
537
|
+
return len;
|
|
538
|
+
}
|
|
539
|
+
sp_int sp_IOBuffer_copy1(sp_IOBuffer *b, sp_RbVal src) { return iob_copy(b, src, 0, 0, 0, 0); }
|
|
540
|
+
sp_int sp_IOBuffer_copy2(sp_IOBuffer *b, sp_RbVal src, sp_int off) { return iob_copy(b, src, off, 0, 0, 0); }
|
|
541
|
+
sp_int sp_IOBuffer_copy3(sp_IOBuffer *b, sp_RbVal src, sp_int off, sp_int len) { return iob_copy(b, src, off, len, 0, 1); }
|
|
542
|
+
sp_int sp_IOBuffer_copy4(sp_IOBuffer *b, sp_RbVal src, sp_int off, sp_int len, sp_int soff) { return iob_copy(b, src, off, len, soff, 1); }
|
|
543
|
+
|
|
544
|
+
static sp_IOBuffer *iob_slice(sp_IOBuffer *b, int64_t off, int64_t len) {
|
|
545
|
+
SP_GC_ROOT(b);
|
|
546
|
+
iob_readable(b);
|
|
547
|
+
if (iob_slice_invalid(b))
|
|
548
|
+
sp_raise_cls("IO::Buffer::InvalidatedError", "Buffer has been invalidated!");
|
|
549
|
+
iob_range(b, off, len);
|
|
550
|
+
sp_IOBuffer *s = iob_alloc_obj(b->cls_id);
|
|
551
|
+
s->source = b->source ? b->source : b;
|
|
552
|
+
s->off = (b->source ? b->off : 0) + off;
|
|
553
|
+
s->size = len;
|
|
554
|
+
s->flags = SP_IOB_SLICE | (b->flags & SP_IOB_READONLY);
|
|
555
|
+
return s;
|
|
556
|
+
}
|
|
557
|
+
sp_IOBuffer *sp_IOBuffer_slice0(sp_IOBuffer *b) { return iob_slice(b, 0, b->size); }
|
|
558
|
+
sp_IOBuffer *sp_IOBuffer_slice1(sp_IOBuffer *b, sp_int off) {
|
|
559
|
+
return iob_slice(b, off, off <= b->size ? b->size - off : 0);
|
|
560
|
+
}
|
|
561
|
+
sp_IOBuffer *sp_IOBuffer_slice2(sp_IOBuffer *b, sp_int off, sp_int len) { return iob_slice(b, off, len); }
|
|
562
|
+
|
|
563
|
+
sp_IOBuffer *sp_IOBuffer_transfer(sp_IOBuffer *b) {
|
|
564
|
+
SP_GC_ROOT(b);
|
|
565
|
+
if (b->flags & SP_IOB_LOCKED)
|
|
566
|
+
sp_raise_cls("IO::Buffer::LockedError", "Cannot transfer ownership of locked buffer!");
|
|
567
|
+
sp_IOBuffer *t = iob_alloc_obj(b->cls_id);
|
|
568
|
+
t->data = b->data;
|
|
569
|
+
t->source = b->source;
|
|
570
|
+
t->off = b->off;
|
|
571
|
+
t->size = b->size;
|
|
572
|
+
t->flags = b->flags;
|
|
573
|
+
t->map_base = b->map_base;
|
|
574
|
+
t->map_len = b->map_len;
|
|
575
|
+
b->data = NULL;
|
|
576
|
+
b->source = NULL;
|
|
577
|
+
b->off = 0;
|
|
578
|
+
b->size = 0;
|
|
579
|
+
b->map_base = NULL;
|
|
580
|
+
b->map_len = 0;
|
|
581
|
+
/* the drained buffer keeps its flags (CRuby inspects as "NULL INTERNAL") */
|
|
582
|
+
return t;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
sp_IOBuffer *sp_IOBuffer_free_m(sp_IOBuffer *b) {
|
|
586
|
+
if (b->flags & SP_IOB_LOCKED)
|
|
587
|
+
sp_raise_cls("IO::Buffer::LockedError", "Buffer is locked!");
|
|
588
|
+
iob_release(b);
|
|
589
|
+
b->source = NULL;
|
|
590
|
+
b->off = 0;
|
|
591
|
+
b->size = 0;
|
|
592
|
+
b->flags = 0;
|
|
593
|
+
return b;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
sp_IOBuffer *sp_IOBuffer_dup_m(sp_IOBuffer *b) {
|
|
597
|
+
SP_GC_ROOT(b);
|
|
598
|
+
iob_readable(b);
|
|
599
|
+
sp_IOBuffer *d = iob_alloc_obj(b->cls_id);
|
|
600
|
+
uint8_t *base = b->source ? iob_ptr(b) : b->data;
|
|
601
|
+
if (base) {
|
|
602
|
+
d->data = (uint8_t *)malloc(b->size > 0 ? (size_t)b->size : 1);
|
|
603
|
+
if (!d->data) sp_oom_die();
|
|
604
|
+
if (b->size > 0) memcpy(d->data, base, (size_t)b->size);
|
|
605
|
+
d->size = b->size;
|
|
606
|
+
d->flags = SP_IOB_INTERNAL;
|
|
607
|
+
}
|
|
608
|
+
return d;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/* ---- comparison ---- */
|
|
612
|
+
|
|
613
|
+
static sp_int iob_cmp_core(sp_IOBuffer *b, sp_IOBuffer *o) {
|
|
614
|
+
iob_readable(b);
|
|
615
|
+
iob_readable(o);
|
|
616
|
+
int64_t n = b->size < o->size ? b->size : o->size;
|
|
617
|
+
if (n > 0) {
|
|
618
|
+
int c = memcmp(iob_ptr(b), iob_ptr(o), (size_t)n);
|
|
619
|
+
if (c) return c < 0 ? -1 : 1;
|
|
620
|
+
}
|
|
621
|
+
return b->size < o->size ? -1 : b->size > o->size ? 1 : 0;
|
|
622
|
+
}
|
|
623
|
+
sp_int sp_IOBuffer_cmp(sp_IOBuffer *b, sp_RbVal other) { return iob_cmp_core(b, iob_other(b, other)); }
|
|
624
|
+
sp_bool sp_IOBuffer_eq(sp_IOBuffer *b, sp_RbVal other) {
|
|
625
|
+
sp_IOBuffer *o = iob_other(b, other);
|
|
626
|
+
if (b->size != o->size) return 0;
|
|
627
|
+
return iob_cmp_core(b, o) == 0;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/* ---- rendering ---- */
|
|
631
|
+
|
|
632
|
+
/* One hexdump line into `out` (which has room): "0x%08x " + width slots of
|
|
633
|
+
"%02x " (or three spaces past the end) + the printable-ASCII column. */
|
|
634
|
+
static size_t iob_hexline(char *out, const uint8_t *base, int64_t addr, int64_t end, int64_t width) {
|
|
635
|
+
char *w = out;
|
|
636
|
+
w += sprintf(w, "0x%08llx ", (unsigned long long)addr);
|
|
637
|
+
for (int64_t i = 0; i < width; i++) {
|
|
638
|
+
if (addr + i < end) w += sprintf(w, " %02x", base[addr + i]);
|
|
639
|
+
else { memcpy(w, " ", 3); w += 3; }
|
|
640
|
+
}
|
|
641
|
+
*w++ = ' ';
|
|
642
|
+
for (int64_t i = 0; i < width && addr + i < end; i++) {
|
|
643
|
+
uint8_t c = base[addr + i];
|
|
644
|
+
*w++ = (c >= 0x20 && c < 0x7f) ? (char)c : '.';
|
|
645
|
+
}
|
|
646
|
+
*w = '\0';
|
|
647
|
+
return (size_t)(w - out);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
static const char *iob_hexdump(sp_IOBuffer *b, int64_t off, int64_t len, int64_t width, int len_given) {
|
|
651
|
+
SP_GC_ROOT(b);
|
|
652
|
+
iob_readable(b);
|
|
653
|
+
if (b->source && iob_slice_invalid(b))
|
|
654
|
+
sp_raise_cls("IO::Buffer::InvalidatedError", "Buffer has been invalidated!");
|
|
655
|
+
if (!iob_base(b)) return NULL;
|
|
656
|
+
if (off < 0) iob_arg("Offset can't be negative!");
|
|
657
|
+
if (width < 1) iob_arg("Width must be at least 1!");
|
|
658
|
+
if (!len_given) len = b->size - off;
|
|
659
|
+
if (len < 0) iob_arg("Length can't be negative!");
|
|
660
|
+
if (off > b->size) off = b->size;
|
|
661
|
+
if (len > b->size - off) len = b->size - off;
|
|
662
|
+
int64_t end = off + len;
|
|
663
|
+
/* ceiling division without the additive form: len + width - 1 itself
|
|
664
|
+
overflows for a near-INT64_MAX width */
|
|
665
|
+
int64_t nlines = len / width + (len % width != 0);
|
|
666
|
+
/* per line: the address prefix ("0x" + up to 16 hex digits + two spaces),
|
|
667
|
+
width * 3 hex slots, a separator, width ASCII chars, the newline and
|
|
668
|
+
NUL -- and `width` is caller data, so every derived size is checked
|
|
669
|
+
before it reaches malloc (a huge width made the product wrap and the
|
|
670
|
+
sprintf write past a too-small block). */
|
|
671
|
+
uint64_t line_max, total;
|
|
672
|
+
if (sp_ckd_mul_u64((uint64_t)width, (uint64_t)4, &line_max) ||
|
|
673
|
+
sp_ckd_add_u64(line_max, (uint64_t)40, &line_max) ||
|
|
674
|
+
sp_ckd_mul_u64((uint64_t)(nlines > 0 ? nlines : 1), line_max, &total) ||
|
|
675
|
+
total > (uint64_t)1 << 40)
|
|
676
|
+
sp_oom_die();
|
|
677
|
+
char *tmp = (char *)malloc((size_t)total);
|
|
678
|
+
if (!tmp) sp_oom_die();
|
|
679
|
+
size_t used = 0;
|
|
680
|
+
const uint8_t *base = iob_base(b);
|
|
681
|
+
for (int64_t a = off; a < end; a += width) {
|
|
682
|
+
if (a > off) tmp[used++] = '\n';
|
|
683
|
+
used += iob_hexline(tmp + used, base, a, end, width);
|
|
684
|
+
}
|
|
685
|
+
char *r = sp_str_alloc_raw(used + 1);
|
|
686
|
+
memcpy(r, tmp, used);
|
|
687
|
+
r[used] = '\0';
|
|
688
|
+
sp_str_set_len(r, used);
|
|
689
|
+
free(tmp);
|
|
690
|
+
return r;
|
|
691
|
+
}
|
|
692
|
+
const char *sp_IOBuffer_hexdump0(sp_IOBuffer *b) { return iob_hexdump(b, 0, 0, 16, 0); }
|
|
693
|
+
const char *sp_IOBuffer_hexdump1(sp_IOBuffer *b, sp_int off) { return iob_hexdump(b, off, 0, 16, 0); }
|
|
694
|
+
const char *sp_IOBuffer_hexdump2(sp_IOBuffer *b, sp_int off, sp_int len) { return iob_hexdump(b, off, len, 16, 1); }
|
|
695
|
+
const char *sp_IOBuffer_hexdump3(sp_IOBuffer *b, sp_int off, sp_int len, sp_int width) { return iob_hexdump(b, off, len, width, 1); }
|
|
696
|
+
|
|
697
|
+
static size_t iob_header(sp_IOBuffer *b, char *out, size_t cap) {
|
|
698
|
+
uint8_t *base = iob_base(b);
|
|
699
|
+
size_t n = (size_t)snprintf(out, cap, "#<IO::Buffer 0x%016llx+%lld",
|
|
700
|
+
(unsigned long long)(uintptr_t)base, (long long)b->size);
|
|
701
|
+
static const struct { uint32_t bit; const char *name; } FLAGS[] = {
|
|
702
|
+
{0, "NULL"}, {SP_IOB_EXTERNAL, "EXTERNAL"}, {SP_IOB_INTERNAL, "INTERNAL"},
|
|
703
|
+
{SP_IOB_MAPPED, "MAPPED"}, {SP_IOB_SHARED, "SHARED"}, {SP_IOB_LOCKED, "LOCKED"},
|
|
704
|
+
{SP_IOB_PRIVATE, "PRIVATE"}, {SP_IOB_READONLY, "READONLY"}, {SP_IOB_SLICE, "SLICE"},
|
|
705
|
+
};
|
|
706
|
+
for (size_t i = 0; i < sizeof FLAGS / sizeof FLAGS[0]; i++) {
|
|
707
|
+
int on = i == 0 ? base == NULL : (b->flags & FLAGS[i].bit) != 0;
|
|
708
|
+
if (on) n += (size_t)snprintf(out + n, cap - n, " %s", FLAGS[i].name);
|
|
709
|
+
}
|
|
710
|
+
n += (size_t)snprintf(out + n, cap - n, ">");
|
|
711
|
+
return n;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
const char *sp_IOBuffer_to_s(sp_IOBuffer *b) {
|
|
715
|
+
SP_GC_ROOT(b);
|
|
716
|
+
char hd[256];
|
|
717
|
+
size_t n = iob_header(b, hd, sizeof hd);
|
|
718
|
+
char *r = sp_str_alloc_raw(n + 1);
|
|
719
|
+
memcpy(r, hd, n + 1);
|
|
720
|
+
sp_str_set_len(r, n);
|
|
721
|
+
return r;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const char *sp_IOBuffer_inspect(sp_IOBuffer *b) {
|
|
725
|
+
SP_GC_ROOT(b);
|
|
726
|
+
char hd[256];
|
|
727
|
+
size_t n = iob_header(b, hd, sizeof hd);
|
|
728
|
+
int64_t shown = b->size < 256 ? b->size : 256;
|
|
729
|
+
if (!iob_base(b) || iob_slice_invalid(b) || shown == 0) {
|
|
730
|
+
char *r0 = sp_str_alloc_raw(n + 1);
|
|
731
|
+
memcpy(r0, hd, n + 1);
|
|
732
|
+
sp_str_set_len(r0, n);
|
|
733
|
+
return r0;
|
|
734
|
+
}
|
|
735
|
+
const char *dump = iob_hexdump(b, 0, shown, 16, 1);
|
|
736
|
+
SP_GC_ROOT_STR(dump);
|
|
737
|
+
size_t dn = sp_str_byte_len(dump);
|
|
738
|
+
char tail[64];
|
|
739
|
+
tail[0] = '\0';
|
|
740
|
+
if (b->size > shown)
|
|
741
|
+
snprintf(tail, sizeof tail, "\n(and %lld more bytes not printed)", (long long)(b->size - shown));
|
|
742
|
+
size_t tn = strlen(tail);
|
|
743
|
+
char *r = sp_str_alloc_raw(n + 1 + dn + tn + 1);
|
|
744
|
+
memcpy(r, hd, n);
|
|
745
|
+
r[n] = '\n';
|
|
746
|
+
memcpy(r + n + 1, dump, dn);
|
|
747
|
+
memcpy(r + n + 1 + dn, tail, tn + 1);
|
|
748
|
+
sp_str_set_len(r, n + 1 + dn + tn);
|
|
749
|
+
return r;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/* ---- predicates ---- */
|
|
753
|
+
|
|
754
|
+
/* a slice's own base pointer is never the null buffer, even when its root
|
|
755
|
+
was freed (CRuby: null? false, valid? false there) */
|
|
756
|
+
sp_bool sp_IOBuffer_null_p(sp_IOBuffer *b) { return b->source ? 0 : b->data == NULL; }
|
|
757
|
+
sp_bool sp_IOBuffer_empty_p(sp_IOBuffer *b) { return b->size == 0; }
|
|
758
|
+
sp_bool sp_IOBuffer_valid_p(sp_IOBuffer *b) { return !iob_slice_invalid(b); }
|
|
759
|
+
sp_bool sp_IOBuffer_external_p(sp_IOBuffer *b) { return (b->flags & SP_IOB_EXTERNAL) != 0; }
|
|
760
|
+
sp_bool sp_IOBuffer_internal_p(sp_IOBuffer *b) { return (b->flags & SP_IOB_INTERNAL) != 0; }
|
|
761
|
+
sp_bool sp_IOBuffer_mapped_p(sp_IOBuffer *b) { return (b->flags & SP_IOB_MAPPED) != 0; }
|
|
762
|
+
sp_bool sp_IOBuffer_shared_p(sp_IOBuffer *b) { return (b->flags & SP_IOB_SHARED) != 0; }
|
|
763
|
+
sp_bool sp_IOBuffer_locked_p(sp_IOBuffer *b) { return (b->flags & SP_IOB_LOCKED) != 0; }
|
|
764
|
+
sp_bool sp_IOBuffer_readonly_p(sp_IOBuffer *b) { return (b->flags & SP_IOB_READONLY) != 0; }
|
|
765
|
+
sp_bool sp_IOBuffer_private_p(sp_IOBuffer *b) { return (b->flags & SP_IOB_PRIVATE) != 0; }
|
|
766
|
+
|
|
767
|
+
/* ---- bitwise ---- */
|
|
768
|
+
|
|
769
|
+
typedef uint8_t (*iob_bop)(uint8_t, uint8_t);
|
|
770
|
+
static uint8_t bop_and(uint8_t a, uint8_t c) { return a & c; }
|
|
771
|
+
static uint8_t bop_or(uint8_t a, uint8_t c) { return a | c; }
|
|
772
|
+
static uint8_t bop_xor(uint8_t a, uint8_t c) { return a ^ c; }
|
|
773
|
+
|
|
774
|
+
/* The mask (second operand) TILES: the result is self-sized, and the mask
|
|
775
|
+
repeats over it. An empty mask has nothing to repeat: MaskError. */
|
|
776
|
+
static sp_IOBuffer *iob_bitop(sp_IOBuffer *b, sp_RbVal otherv, iob_bop op) {
|
|
777
|
+
sp_IOBuffer *o = iob_other(b, otherv);
|
|
778
|
+
SP_GC_ROOT(b);
|
|
779
|
+
SP_GC_ROOT(o);
|
|
780
|
+
iob_readable(b);
|
|
781
|
+
iob_readable(o);
|
|
782
|
+
if (o->size == 0) sp_raise_cls("IO::Buffer::MaskError", "Zero-length mask given!");
|
|
783
|
+
int64_t n = b->size;
|
|
784
|
+
sp_IOBuffer *r = iob_alloc_obj(b->cls_id);
|
|
785
|
+
if (n > 0) {
|
|
786
|
+
r->data = (uint8_t *)malloc((size_t)n);
|
|
787
|
+
if (!r->data) sp_oom_die();
|
|
788
|
+
const uint8_t *pa = iob_ptr(b), *pb = iob_ptr(o);
|
|
789
|
+
for (int64_t i = 0; i < n; i++) r->data[i] = op(pa[i], pb[i % o->size]);
|
|
790
|
+
r->size = n;
|
|
791
|
+
r->flags = SP_IOB_INTERNAL;
|
|
792
|
+
}
|
|
793
|
+
return r;
|
|
794
|
+
}
|
|
795
|
+
sp_IOBuffer *sp_IOBuffer_and(sp_IOBuffer *b, sp_RbVal other) { return iob_bitop(b, other, bop_and); }
|
|
796
|
+
sp_IOBuffer *sp_IOBuffer_or(sp_IOBuffer *b, sp_RbVal other) { return iob_bitop(b, other, bop_or); }
|
|
797
|
+
sp_IOBuffer *sp_IOBuffer_xor(sp_IOBuffer *b, sp_RbVal other) { return iob_bitop(b, other, bop_xor); }
|
|
798
|
+
sp_IOBuffer *sp_IOBuffer_not(sp_IOBuffer *b) {
|
|
799
|
+
SP_GC_ROOT(b);
|
|
800
|
+
iob_readable(b);
|
|
801
|
+
sp_IOBuffer *r = iob_alloc_obj(b->cls_id);
|
|
802
|
+
if (b->size > 0 && (b->source ? iob_ptr(b) : b->data)) {
|
|
803
|
+
r->data = (uint8_t *)malloc((size_t)b->size);
|
|
804
|
+
if (!r->data) sp_oom_die();
|
|
805
|
+
const uint8_t *p = iob_base(b);
|
|
806
|
+
for (int64_t i = 0; i < b->size; i++) r->data[i] = (uint8_t)~p[i];
|
|
807
|
+
r->size = b->size;
|
|
808
|
+
r->flags = SP_IOB_INTERNAL;
|
|
809
|
+
}
|
|
810
|
+
return r;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
static sp_IOBuffer *iob_bitop_ip(sp_IOBuffer *b, sp_RbVal otherv, iob_bop op) {
|
|
814
|
+
sp_IOBuffer *o = iob_other(b, otherv);
|
|
815
|
+
iob_writable(b);
|
|
816
|
+
iob_readable(o);
|
|
817
|
+
if (o->size == 0) sp_raise_cls("IO::Buffer::MaskError", "Zero-length mask given!");
|
|
818
|
+
uint8_t *pa = iob_ptr(b);
|
|
819
|
+
const uint8_t *pb = iob_ptr(o);
|
|
820
|
+
for (int64_t i = 0; i < b->size; i++) pa[i] = op(pa[i], pb[i % o->size]);
|
|
821
|
+
return b;
|
|
822
|
+
}
|
|
823
|
+
sp_IOBuffer *sp_IOBuffer_and_ip(sp_IOBuffer *b, sp_RbVal other) { return iob_bitop_ip(b, other, bop_and); }
|
|
824
|
+
sp_IOBuffer *sp_IOBuffer_or_ip(sp_IOBuffer *b, sp_RbVal other) { return iob_bitop_ip(b, other, bop_or); }
|
|
825
|
+
sp_IOBuffer *sp_IOBuffer_xor_ip(sp_IOBuffer *b, sp_RbVal other) { return iob_bitop_ip(b, other, bop_xor); }
|
|
826
|
+
sp_IOBuffer *sp_IOBuffer_not_ip(sp_IOBuffer *b) {
|
|
827
|
+
iob_writable(b);
|
|
828
|
+
uint8_t *p = b->size > 0 ? iob_ptr(b) : NULL;
|
|
829
|
+
for (int64_t i = 0; i < b->size; i++) p[i] = (uint8_t)~p[i];
|
|
830
|
+
return b;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/* ---- locked { } ---- */
|
|
834
|
+
|
|
835
|
+
sp_IOBuffer *sp_IOBuffer_lock(sp_IOBuffer *b) {
|
|
836
|
+
if (b->flags & SP_IOB_LOCKED) sp_raise_cls("IO::Buffer::LockedError", "Buffer already locked!");
|
|
837
|
+
b->flags |= SP_IOB_LOCKED;
|
|
838
|
+
return b;
|
|
839
|
+
}
|
|
840
|
+
sp_IOBuffer *sp_IOBuffer_unlock(sp_IOBuffer *b) {
|
|
841
|
+
b->flags &= ~SP_IOB_LOCKED;
|
|
842
|
+
return b;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/* ---- FFI ----
|
|
846
|
+
An IO::Buffer passed to an ffi_func pointer argument hands C its base
|
|
847
|
+
address, as CRuby's rb_io_buffer_get_bytes_for_reading / _for_writing do:
|
|
848
|
+
NULL for a null buffer (freed, or zero-sized), InvalidatedError for a
|
|
849
|
+
slice whose source was freed or shrunk under it, and AccessError for a
|
|
850
|
+
read-only buffer in a slot other than :buffer_in. A nil (a buffer-typed
|
|
851
|
+
local that holds none) is NULL, as it is for :ptr. */
|
|
852
|
+
void *sp_IOBuffer_ffi_base(sp_IOBuffer *b, sp_int writing) {
|
|
853
|
+
if (!b) return NULL;
|
|
854
|
+
if (writing) iob_writable(b);
|
|
855
|
+
uint8_t *p = iob_ptr(b); /* an invalidated slice raises first */
|
|
856
|
+
return b->size > 0 ? p : NULL; /* a zero-size view is a null buffer too */
|
|
857
|
+
}
|
|
858
|
+
/* Held across a `blocking: true` call, during which other threads run: the
|
|
859
|
+
buffer, and a slice's source, are locked so that their free or resize
|
|
860
|
+
raises LockedError instead of releasing memory C is still using. Answers
|
|
861
|
+
what it locked (a buffer the program already holds locked stays so), for
|
|
862
|
+
sp_IOBuffer_ffi_release to undo exactly that. */
|
|
863
|
+
sp_int sp_IOBuffer_ffi_hold(sp_IOBuffer *b) {
|
|
864
|
+
sp_int m = 0;
|
|
865
|
+
if (!b) return 0;
|
|
866
|
+
if (!(b->flags & SP_IOB_LOCKED)) { b->flags |= SP_IOB_LOCKED; m |= 1; }
|
|
867
|
+
if (b->source && !(b->source->flags & SP_IOB_LOCKED)) { b->source->flags |= SP_IOB_LOCKED; m |= 2; }
|
|
868
|
+
return m;
|
|
869
|
+
}
|
|
870
|
+
void sp_IOBuffer_ffi_release(sp_IOBuffer *b, sp_int m) {
|
|
871
|
+
if (!b) return;
|
|
872
|
+
if (m & 1) b->flags &= ~SP_IOB_LOCKED;
|
|
873
|
+
if ((m & 2) && b->source) b->source->flags &= ~SP_IOB_LOCKED;
|
|
874
|
+
}
|
|
875
|
+
/* the same for a boxed argument, which may not be a buffer at all */
|
|
876
|
+
sp_int sp_IOBuffer_ffi_hold_v(sp_RbVal v, sp_int cls_id) {
|
|
877
|
+
return v.tag == SP_TAG_OBJ && v.cls_id == cls_id ? sp_IOBuffer_ffi_hold((sp_IOBuffer *)v.v.p) : 0;
|
|
878
|
+
}
|
|
879
|
+
void sp_IOBuffer_ffi_release_v(sp_RbVal v, sp_int cls_id, sp_int m) {
|
|
880
|
+
if (v.tag == SP_TAG_OBJ && v.cls_id == cls_id) sp_IOBuffer_ffi_release((sp_IOBuffer *)v.v.p, m);
|
|
881
|
+
}
|
|
882
|
+
/* a boxed pointer argument: an IO::Buffer (class `cls_id`) gives its base,
|
|
883
|
+
anything else its raw pointer as before */
|
|
884
|
+
void *sp_IOBuffer_ffi_ptr(sp_RbVal v, sp_int cls_id, sp_int writing) {
|
|
885
|
+
if (v.tag == SP_TAG_OBJ && v.cls_id == cls_id && v.v.p)
|
|
886
|
+
return sp_IOBuffer_ffi_base((sp_IOBuffer *)v.v.p, writing);
|
|
887
|
+
return v.v.p;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/* ---- IO integration (#4474) ----
|
|
891
|
+
Spinel's IO is a stdio FILE* (sp_File), so a raw descriptor read has to
|
|
892
|
+
stay coherent with the stdio buffer: bytes stdio already holds for the
|
|
893
|
+
stream are served first (the readpartial / read_nonblock discipline), and
|
|
894
|
+
a raw write flushes what stdio still holds so the bytes do not reorder.
|
|
895
|
+
A read that can block parks the green thread on readiness first, as every
|
|
896
|
+
IO read does, and a write on a pipe or socket parks on writability; the
|
|
897
|
+
Ruby side holds the buffer's lock across the call so a concurrent resize
|
|
898
|
+
or free cannot move the bytes a parked syscall is about to touch. One
|
|
899
|
+
syscall per call, answering its count, 0 at EOF, or -errno, as CRuby. */
|
|
900
|
+
static sp_File *iob_io(sp_RbVal io) {
|
|
901
|
+
if (io.tag == SP_TAG_OBJ && io.cls_id == SP_BUILTIN_IO && io.v.p) {
|
|
902
|
+
sp_File *f = (sp_File *)io.v.p;
|
|
903
|
+
if (!f->fp || f->closed) sp_raise_cls("IOError", "closed stream");
|
|
904
|
+
return f;
|
|
905
|
+
}
|
|
906
|
+
sp_raise_cls("TypeError", sp_sprintf("wrong argument type %s (expected IO)", iob_val_name(io)));
|
|
907
|
+
}
|
|
908
|
+
/* the span [offset, offset+length) of the buffer, length < 0 meaning nil */
|
|
909
|
+
static uint8_t *iob_io_span(sp_IOBuffer *b, sp_int *length, sp_int offset, int writing) {
|
|
910
|
+
if (writing) iob_writable(b);
|
|
911
|
+
if (*length < 0) *length = b->size - offset;
|
|
912
|
+
iob_range(b, offset, *length);
|
|
913
|
+
uint8_t *base = iob_ptr(b);
|
|
914
|
+
if (!base) sp_raise_cls("IO::Buffer::AccessError", "Buffer is not allocated!");
|
|
915
|
+
return base + offset;
|
|
916
|
+
}
|
|
917
|
+
sp_int sp_IOBuffer_read_io(sp_IOBuffer *b, sp_RbVal io, sp_int length, sp_int offset) {
|
|
918
|
+
SP_GC_ROOT(b); SP_GC_ROOT_RBVAL(io);
|
|
919
|
+
sp_File *f = iob_io(io);
|
|
920
|
+
uint8_t *at = iob_io_span(b, &length, offset, 1);
|
|
921
|
+
if (length == 0) return 0;
|
|
922
|
+
/* what stdio already holds is data the stream has delivered: take it
|
|
923
|
+
first, or a raw read would step over it */
|
|
924
|
+
size_t pend = sp_io_stdio_buffered(f->fp);
|
|
925
|
+
if (pend > 0) {
|
|
926
|
+
size_t want = (size_t)length < pend ? (size_t)length : pend;
|
|
927
|
+
return (sp_int)fread(at, 1, want, f->fp);
|
|
928
|
+
}
|
|
929
|
+
sp_io_wait_readable(f);
|
|
930
|
+
at = iob_io_span(b, &length, offset, 1); /* the park can raise, never move a locked buffer */
|
|
931
|
+
ssize_t n;
|
|
932
|
+
do { n = read(fileno(f->fp), at, (size_t)length); } while (n < 0 && errno == EINTR);
|
|
933
|
+
return n < 0 ? -(sp_int)errno : (sp_int)n;
|
|
934
|
+
}
|
|
935
|
+
sp_int sp_IOBuffer_write_io(sp_IOBuffer *b, sp_RbVal io, sp_int length, sp_int offset) {
|
|
936
|
+
SP_GC_ROOT(b); SP_GC_ROOT_RBVAL(io);
|
|
937
|
+
sp_File *f = iob_io(io);
|
|
938
|
+
uint8_t *at = iob_io_span(b, &length, offset, 0);
|
|
939
|
+
if (length == 0) return 0;
|
|
940
|
+
/* a buffered write still in stdio would land after this one */
|
|
941
|
+
if (fflush(f->fp) != 0) return -(sp_int)errno;
|
|
942
|
+
int fd = fileno(f->fp);
|
|
943
|
+
for (;;) {
|
|
944
|
+
ssize_t n = write(fd, at, (size_t)length);
|
|
945
|
+
if (n >= 0) return (sp_int)n;
|
|
946
|
+
if (errno == EINTR) continue;
|
|
947
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) { sp_io_wait_writable(f); continue; }
|
|
948
|
+
return -(sp_int)errno;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
sp_int sp_IOBuffer_pread_io(sp_IOBuffer *b, sp_RbVal io, sp_int from, sp_int length, sp_int offset) {
|
|
952
|
+
SP_GC_ROOT(b); SP_GC_ROOT_RBVAL(io);
|
|
953
|
+
sp_File *f = iob_io(io);
|
|
954
|
+
if (from < 0) iob_arg("Position can't be negative!");
|
|
955
|
+
uint8_t *at = iob_io_span(b, &length, offset, 1);
|
|
956
|
+
if (length == 0) return 0;
|
|
957
|
+
ssize_t n;
|
|
958
|
+
do { n = pread(fileno(f->fp), at, (size_t)length, (off_t)from); } while (n < 0 && errno == EINTR);
|
|
959
|
+
return n < 0 ? -(sp_int)errno : (sp_int)n;
|
|
960
|
+
}
|
|
961
|
+
sp_int sp_IOBuffer_pwrite_io(sp_IOBuffer *b, sp_RbVal io, sp_int from, sp_int length, sp_int offset) {
|
|
962
|
+
SP_GC_ROOT(b); SP_GC_ROOT_RBVAL(io);
|
|
963
|
+
sp_File *f = iob_io(io);
|
|
964
|
+
if (from < 0) iob_arg("Position can't be negative!");
|
|
965
|
+
uint8_t *at = iob_io_span(b, &length, offset, 0);
|
|
966
|
+
if (length == 0) return 0;
|
|
967
|
+
if (fflush(f->fp) != 0) return -(sp_int)errno;
|
|
968
|
+
ssize_t n;
|
|
969
|
+
do { n = pwrite(fileno(f->fp), at, (size_t)length, (off_t)from); } while (n < 0 && errno == EINTR);
|
|
970
|
+
return n < 0 ? -(sp_int)errno : (sp_int)n;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/* IO::Buffer.map(file, size = nil, offset = 0, flags = READONLY): a view of
|
|
974
|
+
the file's bytes through mmap. The mapping is page-aligned below the
|
|
975
|
+
requested offset and `data` points at the offset within it; PRIVATE maps
|
|
976
|
+
copy-on-write, SHARED (the default) writes through, READONLY maps
|
|
977
|
+
PROT_READ. The finalizer munmaps; a slice keeps its source alive as for
|
|
978
|
+
any buffer; resize is refused (see sp_IOBuffer_resize). */
|
|
979
|
+
sp_IOBuffer *sp_IOBuffer_become_map(sp_IOBuffer *b, sp_RbVal io, sp_int size, sp_int offset, sp_int flags) {
|
|
980
|
+
SP_GC_ROOT(b); SP_GC_ROOT_RBVAL(io);
|
|
981
|
+
sp_File *f = iob_io(io);
|
|
982
|
+
if (offset < 0) iob_arg("Offset can't be negative!");
|
|
983
|
+
int fd = fileno(f->fp);
|
|
984
|
+
fflush(f->fp);
|
|
985
|
+
if (size < 0) {
|
|
986
|
+
struct stat st;
|
|
987
|
+
if (fstat(fd, &st) != 0) sp_raise_cls("SystemCallError", sp_sprintf("%s @ IO::Buffer.map", strerror(errno)));
|
|
988
|
+
size = (sp_int)st.st_size - offset;
|
|
989
|
+
if (size < 0) size = 0;
|
|
990
|
+
}
|
|
991
|
+
uint32_t fl = (uint32_t)flags;
|
|
992
|
+
int prot = (fl & SP_IOB_READONLY) ? PROT_READ : (PROT_READ | PROT_WRITE);
|
|
993
|
+
int mflags = (fl & SP_IOB_PRIVATE) ? MAP_PRIVATE : MAP_SHARED;
|
|
994
|
+
iob_release(b);
|
|
995
|
+
b->source = NULL; b->off = 0;
|
|
996
|
+
b->size = size;
|
|
997
|
+
b->flags = SP_IOB_MAPPED | (fl & (SP_IOB_READONLY | SP_IOB_PRIVATE)) | ((fl & SP_IOB_PRIVATE) ? 0 : SP_IOB_SHARED);
|
|
998
|
+
if (size == 0) { b->data = NULL; return b; }
|
|
999
|
+
long ps = sysconf(_SC_PAGESIZE);
|
|
1000
|
+
if (ps <= 0) ps = 4096;
|
|
1001
|
+
off_t page = (off_t)(offset - offset % ps);
|
|
1002
|
+
size_t len = (size_t)(size + (offset - page));
|
|
1003
|
+
void *m = mmap(NULL, len, prot, mflags, fd, page);
|
|
1004
|
+
if (m == MAP_FAILED) {
|
|
1005
|
+
int e = errno;
|
|
1006
|
+
b->size = 0; b->flags = 0;
|
|
1007
|
+
sp_raise_cls(e == EACCES ? "Errno::EACCES" : e == EINVAL ? "Errno::EINVAL" : "SystemCallError",
|
|
1008
|
+
sp_sprintf("%s @ IO::Buffer.map", strerror(e)));
|
|
1009
|
+
}
|
|
1010
|
+
b->map_base = m;
|
|
1011
|
+
b->map_len = len;
|
|
1012
|
+
b->data = (uint8_t *)m + (offset - page);
|
|
1013
|
+
return b;
|
|
1014
|
+
}
|