obj_bytes.c
1 #include <string.h>
2 #include <kuroko/vm.h>
3 #include <kuroko/value.h>
4 #include <kuroko/memory.h>
5 #include <kuroko/util.h>
6 
7 #include "private.h"
8 
9 struct ByteArray {
10  KrkInstance inst;
11  KrkValue actual;
12 };
13 
14 #define AS_bytes(o) AS_BYTES(o)
15 #define CURRENT_CTYPE KrkBytes *
16 #define CURRENT_NAME self
17 
18 #undef IS_bytes
19 #define IS_bytes(o) (IS_BYTES(o) || krk_isInstanceOf(o, vm.baseClasses->bytesClass))
20 
21 static int _bytes_callback(void * context, const KrkValue * values, size_t count) {
22  struct StringBuilder * sb = context;
23  for (size_t i = 0; i < count; ++i) {
24  if (!IS_INTEGER(values[i])) {
25  krk_runtimeError(vm.exceptions->typeError, "'%T' is not an integer", values[i]);
26  return 1;
27  }
28  if (AS_INTEGER(values[i]) < 0 || AS_INTEGER(values[i]) > 255) {
29  krk_runtimeError(vm.exceptions->typeError, "bytes object must be in range(0, 256)");
30  return 1;
31  }
32  pushStringBuilder(sb, AS_INTEGER(values[i]));
33  }
34  return 0;
35 }
36 
37 KRK_StaticMethod(bytes,__new__) {
38  if (argc < 2) return OBJECT_VAL(krk_newBytes(0,NULL));
39  METHOD_TAKES_AT_MOST(1);
40 
41  if (IS_bytearray(argv[1])) {
42  return OBJECT_VAL(krk_newBytes(
43  AS_BYTES(AS_bytearray(argv[1])->actual)->length,
44  AS_BYTES(AS_bytearray(argv[1])->actual)->bytes));
45  } else if (IS_STRING(argv[1])) {
46  return OBJECT_VAL(krk_newBytes(AS_STRING(argv[1])->length, (uint8_t*)AS_CSTRING(argv[1])));
47  } else if (IS_INTEGER(argv[1])) {
48  if (AS_INTEGER(argv[1]) < 0) return krk_runtimeError(vm.exceptions->valueError, "negative count");
49  return OBJECT_VAL(krk_newBytes(AS_INTEGER(argv[1]),NULL));
50  } else {
51  struct StringBuilder sb = {0};
52  if (krk_unpackIterable(argv[1], &sb, _bytes_callback)) return NONE_VAL();
53  return finishStringBuilderBytes(&sb);
54  }
55 }
56 
57 #undef IS_bytes
58 #define IS_bytes(o) IS_BYTES(o)
59 
60 KRK_Method(bytes,__hash__) {
61  METHOD_TAKES_NONE();
62  uint32_t hash = 0;
63  /* This is the so-called "sdbm" hash. It comes from a piece of
64  * public domain code from a clone of ndbm. */
65  for (size_t i = 0; i < self->length; ++i) {
66  krk_hash_advance(hash,self->bytes[i]);
67  }
68  return INTEGER_VAL(hash);
69 }
70 
71 /* bytes objects are not interned; need to do this the old-fashioned way. */
72 KRK_Method(bytes,__eq__) {
73  if (!IS_BYTES(argv[1])) return BOOLEAN_VAL(0);
74  KrkBytes * them = AS_BYTES(argv[1]);
75  if (self->length != them->length) return BOOLEAN_VAL(0);
76  if (self->obj.hash != them->obj.hash) return BOOLEAN_VAL(0);
77  for (size_t i = 0; i < self->length; ++i) {
78  if (self->bytes[i] != them->bytes[i]) return BOOLEAN_VAL(0);
79  }
80  return BOOLEAN_VAL(1);
81 }
82 
83 #define AT_END() (self->length == 0 || i == self->length - 1)
84 
85 KRK_Method(bytes,__repr__) {
86  struct StringBuilder sb = {0};
87 
88  pushStringBuilder(&sb, 'b');
89  pushStringBuilder(&sb, '\'');
90 
91  for (size_t i = 0; i < AS_BYTES(argv[0])->length; ++i) {
92  uint8_t ch = AS_BYTES(argv[0])->bytes[i];
93  switch (ch) {
94  case '\\': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, '\\'); break;
95  case '\'': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, '\''); break;
96  case '\a': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'a'); break;
97  case '\b': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'b'); break;
98  case '\f': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'f'); break;
99  case '\n': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'n'); break;
100  case '\r': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'r'); break;
101  case '\t': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 't'); break;
102  case '\v': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'v'); break;
103  default: {
104  if (ch < ' ' || ch >= 0x7F) {
105  pushStringBuilder(&sb, '\\');
106  pushStringBuilder(&sb, 'x');
107  char hex[3];
108  snprintf(hex,3,"%02x", ch);
109  pushStringBuilder(&sb, hex[0]);
110  pushStringBuilder(&sb, hex[1]);
111  } else {
112  pushStringBuilder(&sb, ch);
113  }
114  break;
115  }
116  }
117  }
118 
119  pushStringBuilder(&sb, '\'');
120 
121  return finishStringBuilder(&sb);
122 }
123 
124 KRK_Method(bytes,__getitem__) {
125  METHOD_TAKES_EXACTLY(1);
126 
127  if (IS_INTEGER(argv[1])) {
128  CHECK_ARG(1,int,krk_integer_type,asInt);
129 
130  if (asInt < 0) asInt += (long)self->length;
131  if (asInt < 0 || asInt >= (long)self->length) {
132  return krk_runtimeError(vm.exceptions->indexError, "bytes index out of range: %d", (int)asInt);
133  }
134 
135  return INTEGER_VAL(self->bytes[asInt]);
136 
137  } else if (IS_slice(argv[1])) {
138  KRK_SLICER(argv[1],self->length) {
139  return NONE_VAL();
140  }
141 
142  if (step == 1) {
143  krk_integer_type len = end - start;
144  return OBJECT_VAL(krk_newBytes(len, &self->bytes[start]));
145  } else {
146  struct StringBuilder sb = {0};
147  krk_integer_type i = start;
148  while ((step < 0) ? (i > end) : (i < end)) {
149  pushStringBuilder(&sb, self->bytes[i]);
150  i += step;
151  }
152  return finishStringBuilderBytes(&sb);
153  }
154  } else {
155  return TYPE_ERROR(int or slice, argv[1]);
156  }
157 }
158 
159 KRK_Method(bytes,__len__) {
160  return INTEGER_VAL(AS_BYTES(argv[0])->length);
161 }
162 
163 KRK_Method(bytes,__contains__) {
164  METHOD_TAKES_EXACTLY(1);
165 
166  if (IS_BYTES(argv[1])) {
167  return krk_runtimeError(vm.exceptions->notImplementedError, "not implemented: bytes.__contains__(bytes)");
168  }
169 
170  if (!IS_INTEGER(argv[1])) {
171  return TYPE_ERROR(int,argv[1]);
172  }
173 
174  krk_integer_type val = AS_INTEGER(argv[1]);
175  if (val < 0 || val > 255) {
176  return krk_runtimeError(vm.exceptions->valueError, "byte must be in range(0, 256)");
177  }
178 
179  for (size_t i = 0; i < self->length; ++i) {
180  if (self->bytes[i] == val) return BOOLEAN_VAL(1);
181  }
182 
183  return BOOLEAN_VAL(0);
184 }
185 
186 KRK_Method(bytes,decode) {
187  METHOD_TAKES_NONE();
188  return OBJECT_VAL(krk_copyString((char*)AS_BYTES(argv[0])->bytes, AS_BYTES(argv[0])->length));
189 }
190 
192  struct StringBuilder * sb;
193  KrkBytes * self;
194  int isFirst;
195 };
196 
197 static int _bytes_join_callback(void * context, const KrkValue * values, size_t count) {
198  struct _bytes_join_context * _context = context;
199 
200  for (size_t i = 0; i < count; ++i) {
201  if (!IS_BYTES(values[i])) {
202  krk_runtimeError(vm.exceptions->typeError, "%s() expects %s, not '%T'",
203  "join", "bytes", values[i]);
204  return 1;
205  }
206 
207  if (_context->isFirst) {
208  _context->isFirst = 0;
209  } else {
210  pushStringBuilderStr(_context->sb, (char*)_context->self->bytes, _context->self->length);
211  }
212  pushStringBuilderStr(_context->sb, (char*)AS_BYTES(values[i])->bytes, AS_BYTES(values[i])->length);
213  }
214 
215  return 0;
216 }
217 
218 KRK_Method(bytes,join) {
219  METHOD_TAKES_EXACTLY(1);
220 
221  struct StringBuilder sb = {0};
222 
223  struct _bytes_join_context context = {&sb, self, 1};
224 
225  if (krk_unpackIterable(argv[1], &context, _bytes_join_callback)) {
226  discardStringBuilder(&sb);
227  return NONE_VAL();
228  }
229 
230  return finishStringBuilderBytes(&sb);
231 }
232 
233 KRK_Method(bytes,__add__) {
234  METHOD_TAKES_EXACTLY(1);
235  CHECK_ARG(1,bytes,KrkBytes*,them);
236 
237  struct StringBuilder sb = {0};
238  pushStringBuilderStr(&sb, (char*)self->bytes, self->length);
239  pushStringBuilderStr(&sb, (char*)them->bytes, them->length);
240 
241  return finishStringBuilderBytes(&sb);
242 }
243 
244 FUNC_SIG(bytesiterator,__init__);
245 
246 KRK_Method(bytes,__iter__) {
247  METHOD_TAKES_NONE();
248  KrkInstance * output = krk_newInstance(vm.baseClasses->bytesiteratorClass);
249 
250  krk_push(OBJECT_VAL(output));
251  FUNC_NAME(bytesiterator,__init__)(2, (KrkValue[]){krk_peek(0), argv[0]},0);
252  krk_pop();
253 
254  return OBJECT_VAL(output);
255 }
256 
257 #undef CURRENT_CTYPE
258 
260  KrkInstance inst;
261  KrkValue l;
262  size_t i;
263 };
264 
265 #define CURRENT_CTYPE struct BytesIterator *
266 #define IS_bytesiterator(o) krk_isInstanceOf(o,vm.baseClasses->bytesiteratorClass)
267 #define AS_bytesiterator(o) (struct BytesIterator*)AS_OBJECT(o)
268 
269 static void _bytesiterator_gcscan(KrkInstance * self) {
270  krk_markValue(((struct BytesIterator*)self)->l);
271 }
272 
273 KRK_Method(bytesiterator,__init__) {
274  METHOD_TAKES_EXACTLY(1);
275  CHECK_ARG(1,bytes,KrkBytes*,bytes);
276  self->l = argv[1];
277  self->i = 0;
278  return NONE_VAL();
279 }
280 
281 KRK_Method(bytesiterator,__call__) {
282  KrkValue _list = self->l;
283  size_t _counter = self->i;
284  if (!IS_BYTES(_list) || _counter >= AS_BYTES(_list)->length) {
285  return argv[0];
286  } else {
287  self->i = _counter + 1;
288  return INTEGER_VAL(AS_BYTES(_list)->bytes[_counter]);
289  }
290 }
291 
292 #undef CURRENT_CTYPE
293 #define CURRENT_CTYPE struct ByteArray *
294 
295 static void _bytearray_gcscan(KrkInstance * self) {
296  krk_markValue(((struct ByteArray*)self)->actual);
297 }
298 
299 KRK_Method(bytearray,__init__) {
300  METHOD_TAKES_AT_MOST(1);
301  if (argc < 2) {
302  self->actual = OBJECT_VAL(krk_newBytes(0,NULL));
303  } else if (IS_BYTES(argv[1])) {
304  self->actual = OBJECT_VAL(krk_newBytes(AS_BYTES(argv[1])->length, AS_BYTES(argv[1])->bytes));
305  } else if (IS_INTEGER(argv[1])) {
306  self->actual = OBJECT_VAL(krk_newBytes(AS_INTEGER(argv[1]),NULL));
307  memset(AS_BYTES(self->actual)->bytes, 0, AS_BYTES(self->actual)->length);
308  } else {
309  return krk_runtimeError(vm.exceptions->valueError, "expected bytes");
310  }
311  return NONE_VAL();
312 }
313 
314 #undef IS_bytearray
315 #define IS_bytearray(o) (krk_isInstanceOf(o,vm.baseClasses->bytearrayClass) && IS_BYTES(AS_bytearray(o)->actual))
316 
317 /* bytes objects are not interned; need to do this the old-fashioned way. */
318 KRK_Method(bytearray,__eq__) {
319  if (!IS_bytearray(argv[1])) return BOOLEAN_VAL(0);
320  struct ByteArray * them = AS_bytearray(argv[1]);
321  return BOOLEAN_VAL(krk_valuesEqual(self->actual, them->actual));
322 }
323 
324 KRK_Method(bytearray,__repr__) {
325  METHOD_TAKES_NONE();
326  struct StringBuilder sb = {0};
327  pushStringBuilderStr(&sb, "bytearray(", 10);
328  if (!krk_pushStringBuilderFormat(&sb, "%R", self->actual)) {
330  return NONE_VAL();
331  }
332  pushStringBuilder(&sb,')');
333  return finishStringBuilder(&sb);
334 }
335 
336 KRK_Method(bytearray,__getitem__) {
337  METHOD_TAKES_EXACTLY(1);
338 
339  if (IS_INTEGER(argv[1])) {
340  CHECK_ARG(1,int,krk_integer_type,asInt);
341 
342  if (asInt < 0) asInt += (long)AS_BYTES(self->actual)->length;
343  if (asInt < 0 || asInt >= (long)AS_BYTES(self->actual)->length) {
344  return krk_runtimeError(vm.exceptions->indexError, "bytearray index out of range: %d", (int)asInt);
345  }
346 
347  return INTEGER_VAL(AS_BYTES(self->actual)->bytes[asInt]);
348  } else if (IS_slice(argv[1])) {
349  KRK_SLICER(argv[1],AS_BYTES(self->actual)->length) {
350  return NONE_VAL();
351  }
352 
353  if (step == 1) {
354  krk_integer_type len = end - start;
355  return OBJECT_VAL(krk_newBytes(len, &AS_BYTES(self->actual)->bytes[start]));
356  } else {
357  struct StringBuilder sb = {0};
358  krk_integer_type i = start;
359  while ((step < 0) ? (i > end) : (i < end)) {
360  pushStringBuilder(&sb, AS_BYTES(self->actual)->bytes[i]);
361  i += step;
362  }
363  return finishStringBuilderBytes(&sb);
364  }
365 
366  } else {
367  return TYPE_ERROR(int or slice, argv[1]);
368  }
369 }
370 
371 KRK_Method(bytearray,__setitem__) {
372  METHOD_TAKES_EXACTLY(2);
373  CHECK_ARG(1,int,krk_integer_type,asInt);
374  CHECK_ARG(2,int,krk_integer_type,val);
375 
376  if (asInt < 0) asInt += (long)AS_BYTES(self->actual)->length;
377  if (asInt < 0 || asInt >= (long)AS_BYTES(self->actual)->length) {
378  return krk_runtimeError(vm.exceptions->indexError, "bytearray index out of range: %d", (int)asInt);
379  }
380  AS_BYTES(self->actual)->bytes[asInt] = val;
381 
382  return INTEGER_VAL(AS_BYTES(self->actual)->bytes[asInt]);
383 }
384 
385 KRK_Method(bytearray,__len__) {
386  return INTEGER_VAL(AS_BYTES(self->actual)->length);
387 }
388 
389 KRK_Method(bytearray,__contains__) {
390  METHOD_TAKES_EXACTLY(1);
391  CHECK_ARG(1,int,krk_integer_type,val);
392  for (size_t i = 0; i < AS_BYTES(self->actual)->length; ++i) {
393  if (AS_BYTES(self->actual)->bytes[i] == val) return BOOLEAN_VAL(1);
394  }
395  return BOOLEAN_VAL(0);
396 }
397 
398 KRK_Method(bytearray,decode) {
399  METHOD_TAKES_NONE();
400  return OBJECT_VAL(krk_copyString((char*)AS_BYTES(self->actual)->bytes, AS_BYTES(self->actual)->length));
401 }
402 
403 KRK_Method(bytearray,__iter__) {
404  METHOD_TAKES_NONE();
405  KrkInstance * output = krk_newInstance(vm.baseClasses->bytesiteratorClass);
406 
407  krk_push(OBJECT_VAL(output));
408  FUNC_NAME(bytesiterator,__init__)(2, (KrkValue[]){krk_peek(0), self->actual},0);
409  krk_pop();
410 
411  return OBJECT_VAL(output);
412 }
413 
414 
415 _noexport
416 void _createAndBind_bytesClass(void) {
417  KrkClass * bytes = ADD_BASE_CLASS(vm.baseClasses->bytesClass, "bytes", vm.baseClasses->objectClass);
418  bytes->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
419  bytes->allocSize = 0;
420  KRK_DOC(BIND_STATICMETHOD(bytes,__new__),
421  "@brief An array of bytes.\n"
422  "@arguments iter=None\n\n"
423  "Creates a new @ref bytes object. If @p iter is provided, it should be a @ref tuple or @ref list "
424  "of integers within the range @c 0 and @c 255.");
425  BIND_METHOD(bytes,__repr__);
426  BIND_METHOD(bytes,__len__);
427  BIND_METHOD(bytes,__contains__);
428  BIND_METHOD(bytes,__getitem__);
429  BIND_METHOD(bytes,__eq__);
430  BIND_METHOD(bytes,__add__);
431  BIND_METHOD(bytes,__iter__);
432  BIND_METHOD(bytes,__hash__);
433  BIND_METHOD(bytes,decode);
434  BIND_METHOD(bytes,join);
435  krk_finalizeClass(bytes);
436 
437  KrkClass * bytesiterator = ADD_BASE_CLASS(vm.baseClasses->bytesiteratorClass, "bytesiterator", vm.baseClasses->objectClass);
438  bytesiterator->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
439  bytesiterator->allocSize = sizeof(struct BytesIterator);
440  bytesiterator->_ongcscan = _bytesiterator_gcscan;
441  BIND_METHOD(bytesiterator,__init__);
442  BIND_METHOD(bytesiterator,__call__);
443  krk_finalizeClass(bytesiterator);
444 
445  KrkClass * bytearray = ADD_BASE_CLASS(vm.baseClasses->bytearrayClass, "bytearray", vm.baseClasses->objectClass);
446  bytearray->allocSize = sizeof(struct ByteArray);
447  bytearray->_ongcscan = _bytearray_gcscan;
448  KRK_DOC(BIND_METHOD(bytearray,__init__),
449  "@brief A mutable array of bytes.\n"
450  "@arguments bytes=None");
451  BIND_METHOD(bytearray,__repr__);
452  BIND_METHOD(bytearray,__len__);
453  BIND_METHOD(bytearray,__contains__);
454  BIND_METHOD(bytearray,__getitem__);
455  BIND_METHOD(bytearray,__setitem__);
456  BIND_METHOD(bytearray,__eq__);
457  BIND_METHOD(bytearray,__iter__);
458  BIND_METHOD(bytearray,decode);
459  krk_finalizeClass(bytearray);
460 }
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Definition: exceptions.c:460
Functions for dealing with garbage collection and memory allocation.
void krk_markValue(KrkValue value)
During a GC scan cycle, mark a value as used.
Definition: memory.c:334
Internal header.
Immutable sequence of bytes.
Definition: object.h:105
KrkObj obj
Base.
Definition: object.h:106
size_t length
Length of data in bytes.
Definition: object.h:107
uint8_t * bytes
Pointer to separately-stored bytes data.
Definition: object.h:108
KrkBytes * krk_newBytes(size_t length, uint8_t *source)
Create a new byte array.
Definition: object.c:367
Type object.
Definition: object.h:215
KrkCleanupCallback _ongcscan
C function to call when the garbage collector visits an instance of this class in the scan phase.
Definition: object.h:223
size_t allocSize
Size to allocate when creating instances of this class.
Definition: object.h:222
KrkObj obj
Base.
Definition: object.h:216
void krk_finalizeClass(KrkClass *_class)
Finalize a class by collecting pointers to core methods.
Definition: vm.c:189
An object of a class.
Definition: object.h:281
KrkInstance * krk_newInstance(KrkClass *_class)
Create a new instance of the given class.
Definition: object.c:343
uint32_t hash
Cached hash value for table keys.
Definition: object.h:44
uint16_t flags
General object flags, mostly related to garbage collection.
Definition: object.h:43
KrkString * krk_copyString(const char *chars, size_t length)
Obtain a string object representation of the given C string.
Definition: object.c:224
Stack reference or primative value.
int krk_valuesEqual(KrkValue a, KrkValue b)
Compare two values for equality.
Definition: value.c:106
Inline flexible string array.
Definition: util.h:162
Utilities for creating native bindings.
KrkValue krk_discardStringBuilder(struct StringBuilder *sb)
Discard the contents of a string builder.
Definition: obj_str.c:1123
int krk_unpackIterable(KrkValue iterable, void *context, int callback(void *, const KrkValue *, size_t))
Unpack an iterable.
Definition: builtins.c:387
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Definition: util.h:304
Definitions for primitive stack references.
Core API for the bytecode virtual machine.
#define vm
Convenience macro for namespacing.
Definition: vm.h:257
KrkValue krk_pop(void)
Pop the top of the stack.
Definition: vm.c:131
void krk_push(KrkValue value)
Push a stack value.
Definition: vm.c:118
KrkValue krk_peek(int distance)
Peek down from the top of the stack.
Definition: vm.c:139