From b4887a78b0e8f5d2883c73c0935548d086a7ca9c Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Wed, 13 Oct 2021 20:52:39 +0900 Subject: [PATCH] Extended bytes, bytesiterator; added bytearray --- src/kuroko/util.h | 4 +- src/kuroko/vm.h | 1 + src/obj_bytes.c | 212 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 191 insertions(+), 26 deletions(-) diff --git a/src/kuroko/util.h b/src/kuroko/util.h index 8d25e3a..baaed60 100644 --- a/src/kuroko/util.h +++ b/src/kuroko/util.h @@ -207,8 +207,8 @@ static inline KrkValue discardStringBuilder(struct StringBuilder * sb) { #define IS_dictkeys(o) krk_isInstanceOf(o,vm.baseClasses->dictkeysClass) #define AS_dictkeys(o) ((struct DictKeys*)AS_OBJECT(o)) -#define IS_bytesiterator(o) (krk_isInstanceOf(o,vm.baseClasses->bytesiteratorClass)) -#define AS_bytesiterator(o) (AS_INSTANCE(o)) +#define IS_bytearray(o) (krk_isInstanceOf(o,vm.baseClasses->bytearrayClass)) +#define AS_bytearray(o) ((struct ByteArray*)AS_INSTANCE(o)) #ifndef unpackError #define unpackError(fromInput) return krk_runtimeError(vm.exceptions->typeError, "'%s' object is not iterable", krk_typeName(fromInput)); diff --git a/src/kuroko/vm.h b/src/kuroko/vm.h index 5a0d521..e2da3f7 100644 --- a/src/kuroko/vm.h +++ b/src/kuroko/vm.h @@ -166,6 +166,7 @@ struct BaseClasses { KrkClass * codeobjectClass; /**< Static compiled bytecode container (KrkCodeObject) */ KrkClass * generatorClass; /**< Generator object. */ KrkClass * notImplClass; /**< NotImplementedType */ + KrkClass * bytearrayClass; /**< Mutable array of bytes */ }; /** diff --git a/src/obj_bytes.c b/src/obj_bytes.c index 54b0fb7..36c6836 100644 --- a/src/obj_bytes.c +++ b/src/obj_bytes.c @@ -4,6 +4,11 @@ #include #include +struct ByteArray { + KrkInstance inst; + KrkValue actual; +}; + #define AS_bytes(o) AS_BYTES(o) #define CURRENT_CTYPE KrkBytes * #define CURRENT_NAME self @@ -39,6 +44,10 @@ KRK_METHOD(bytes,__init__,{ out->bytes[i] = AS_INTEGER(AS_LIST(argv[1])->values[i]); } return krk_pop(); + } else if (IS_bytearray(argv[1])) { + return OBJECT_VAL(krk_newBytes( + AS_BYTES(AS_bytearray(argv[1])->actual)->length, + AS_BYTES(AS_bytearray(argv[1])->actual)->bytes)); } return krk_runtimeError(vm.exceptions->typeError, "Can not convert '%s' to bytes", krk_typeName(argv[1])); @@ -177,6 +186,25 @@ KRK_METHOD(bytes,__add__,{ return finishStringBuilderBytes(&sb); }) +#define BYTES_WRAP_SOFT(val) \ + if (val < 0) val += self->length; \ + if (val < 0) val = 0; \ + if (val > (krk_integer_type)self->length) val = self->length + +KRK_METHOD(bytes,__getslice__,{ + METHOD_TAKES_EXACTLY(2); + if (!(IS_INTEGER(argv[1]) || IS_NONE(argv[1]))) return TYPE_ERROR(int or None, argv[1]); + if (!(IS_INTEGER(argv[2]) || IS_NONE(argv[2]))) return TYPE_ERROR(int or None, argv[2]); + krk_integer_type start = IS_NONE(argv[1]) ? 0 : AS_INTEGER(argv[1]); + krk_integer_type end = IS_NONE(argv[2]) ? (krk_integer_type)self->length : AS_INTEGER(argv[2]); + BYTES_WRAP_SOFT(start); + BYTES_WRAP_SOFT(end); + if (end < start) end = start; + krk_integer_type len = end - start; + + return OBJECT_VAL(krk_newBytes(len, &self->bytes[start])); +}) + FUNC_SIG(bytesiterator,__init__); KRK_METHOD(bytes,__iter__,{ @@ -191,40 +219,155 @@ KRK_METHOD(bytes,__iter__,{ }) #undef CURRENT_CTYPE -#define CURRENT_CTYPE KrkInstance * + +struct BytesIterator { + KrkInstance inst; + KrkValue l; + size_t i; +}; + +#define CURRENT_CTYPE struct BytesIterator * +#define IS_bytesiterator(o) krk_isInstanceOf(o,vm.baseClasses->bytesiteratorClass) +#define AS_bytesiterator(o) (struct BytesIterator*)AS_OBJECT(o) + +static void _bytesiterator_gcscan(KrkInstance * self) { + krk_markValue(((struct BytesIterator*)self)->l); +} + KRK_METHOD(bytesiterator,__init__,{ METHOD_TAKES_EXACTLY(1); - CHECK_ARG(1,bytes,KrkBytes*,base); - krk_push(OBJECT_VAL(self)); - krk_attachNamedObject(&self->fields, "s", (KrkObj*)base); - krk_attachNamedValue(&self->fields, "i", INTEGER_VAL(0)); - return krk_pop(); + CHECK_ARG(1,bytes,KrkBytes*,bytes); + self->l = argv[1]; + self->i = 0; + return argv[0]; }) KRK_METHOD(bytesiterator,__call__,{ - METHOD_TAKES_NONE(); - KrkValue _bytes; - KrkValue _counter; - const char * errorStr = NULL; - if (!krk_tableGet(&self->fields, OBJECT_VAL(S("s")), &_bytes)) { - errorStr = "no str pointer"; - goto _corrupt; - } - if (!krk_tableGet(&self->fields, OBJECT_VAL(S("i")), &_counter)) { - errorStr = "no index"; - goto _corrupt; - } - - if ((size_t)AS_INTEGER(_counter) >= AS_BYTES(_bytes)->length) { + KrkValue _list = self->l; + size_t _counter = self->i; + if (_counter >= AS_BYTES(_list)->length) { return argv[0]; } else { - krk_attachNamedValue(&self->fields, "i", INTEGER_VAL(AS_INTEGER(_counter)+1)); - return INTEGER_VAL(AS_BYTES(_bytes)->bytes[AS_INTEGER(_counter)]); + self->i = _counter + 1; + return INTEGER_VAL(AS_BYTES(_list)->bytes[_counter]); } -_corrupt: - return krk_runtimeError(vm.exceptions->typeError, "Corrupt bytes iterator: %s", errorStr); }) +#undef CURRENT_CTYPE +#define CURRENT_CTYPE struct ByteArray * + +static void _bytearray_gcscan(KrkInstance * self) { + krk_markValue(((struct ByteArray*)self)->actual); +} + +KRK_METHOD(bytearray,__init__,{ + METHOD_TAKES_AT_MOST(1); + if (argc < 2) { + self->actual = OBJECT_VAL(krk_newBytes(0,NULL)); + } else if (IS_BYTES(argv[1])) { + self->actual = OBJECT_VAL(krk_newBytes(AS_BYTES(argv[1])->length, AS_BYTES(argv[1])->bytes)); + } else if (IS_INTEGER(argv[1])) { + self->actual = OBJECT_VAL(krk_newBytes(AS_INTEGER(argv[1]),NULL)); + } else { + return krk_runtimeError(vm.exceptions->valueError, "expected bytes"); + } + return argv[0]; +}) + +/* bytes objects are not interned; need to do this the old-fashioned way. */ +KRK_METHOD(bytearray,__eq__,{ + if (!IS_bytearray(argv[1])) return BOOLEAN_VAL(0); + struct ByteArray * them = AS_bytearray(argv[1]); + return BOOLEAN_VAL(krk_valuesEqual(self->actual, them->actual)); +}) + +KRK_METHOD(bytearray,__repr__,{ + METHOD_TAKES_NONE(); + struct StringBuilder sb = {0}; + pushStringBuilderStr(&sb, "bytearray(", 10); + + krk_push(self->actual); + KrkValue repred_bytes = krk_callDirect(vm.baseClasses->bytesClass->_reprer, 1); + pushStringBuilderStr(&sb, AS_STRING(repred_bytes)->chars, AS_STRING(repred_bytes)->length); + pushStringBuilder(&sb,')'); + return finishStringBuilder(&sb); +}) + +KRK_METHOD(bytearray,__getitem__,{ + METHOD_TAKES_EXACTLY(1); + CHECK_ARG(1,int,krk_integer_type,asInt); + + if (asInt < 0) asInt += (long)AS_BYTES(self->actual)->length; + if (asInt < 0 || asInt >= (long)AS_BYTES(self->actual)->length) { + return krk_runtimeError(vm.exceptions->indexError, "bytearray index out of range: %d", (int)asInt); + } + + return INTEGER_VAL(AS_BYTES(self->actual)->bytes[asInt]); +}) + +KRK_METHOD(bytearray,__setitem__,{ + METHOD_TAKES_EXACTLY(2); + CHECK_ARG(1,int,krk_integer_type,asInt); + CHECK_ARG(2,int,krk_integer_type,val); + + if (asInt < 0) asInt += (long)AS_BYTES(self->actual)->length; + if (asInt < 0 || asInt >= (long)AS_BYTES(self->actual)->length) { + return krk_runtimeError(vm.exceptions->indexError, "bytearray index out of range: %d", (int)asInt); + } + AS_BYTES(self->actual)->bytes[asInt] = val; + + return INTEGER_VAL(AS_BYTES(self->actual)->bytes[asInt]); +}) + +KRK_METHOD(bytearray,__len__,{ + return INTEGER_VAL(AS_BYTES(self->actual)->length); +}) + +KRK_METHOD(bytearray,__contains__,{ + METHOD_TAKES_EXACTLY(1); + CHECK_ARG(1,int,krk_integer_type,val); + for (size_t i = 0; i < AS_BYTES(self->actual)->length; ++i) { + if (AS_BYTES(self->actual)->bytes[i] == val) return BOOLEAN_VAL(1); + } + return BOOLEAN_VAL(0); +}) + +KRK_METHOD(bytearray,decode,{ + METHOD_TAKES_NONE(); + return OBJECT_VAL(krk_copyString((char*)AS_BYTES(self->actual)->bytes, AS_BYTES(self->actual)->length)); +}) + +#define BYTEARRAY_WRAP_SOFT(val) \ + if (val < 0) val += AS_BYTES(self->actual)->length; \ + if (val < 0) val = 0; \ + if (val > (krk_integer_type)AS_BYTES(self->actual)->length) val = AS_BYTES(self->actual)->length + +KRK_METHOD(bytearray,__getslice__,{ + METHOD_TAKES_EXACTLY(2); + if (!(IS_INTEGER(argv[1]) || IS_NONE(argv[1]))) return TYPE_ERROR(int or None, argv[1]); + if (!(IS_INTEGER(argv[2]) || IS_NONE(argv[2]))) return TYPE_ERROR(int or None, argv[2]); + krk_integer_type start = IS_NONE(argv[1]) ? 0 : AS_INTEGER(argv[1]); + krk_integer_type end = IS_NONE(argv[2]) ? (krk_integer_type)AS_BYTES(self->actual)->length : AS_INTEGER(argv[2]); + BYTEARRAY_WRAP_SOFT(start); + BYTEARRAY_WRAP_SOFT(end); + if (end < start) end = start; + krk_integer_type len = end - start; + + return OBJECT_VAL(krk_newBytes(len, &AS_BYTES(self->actual)->bytes[start])); +}) + +KRK_METHOD(bytearray,__iter__,{ + METHOD_TAKES_NONE(); + KrkInstance * output = krk_newInstance(vm.baseClasses->bytesiteratorClass); + + krk_push(OBJECT_VAL(output)); + FUNC_NAME(bytesiterator,__init__)(2, (KrkValue[]){krk_peek(0), self->actual},0); + krk_pop(); + + return OBJECT_VAL(output); +}) + + _noexport void _createAndBind_bytesClass(void) { KrkClass * bytes = ADD_BASE_CLASS(vm.baseClasses->bytesClass, "bytes", vm.baseClasses->objectClass); @@ -237,6 +380,7 @@ void _createAndBind_bytesClass(void) { BIND_METHOD(bytes,__len__); BIND_METHOD(bytes,__contains__); BIND_METHOD(bytes,__getitem__); + BIND_METHOD(bytes,__getslice__); BIND_METHOD(bytes,__eq__); BIND_METHOD(bytes,__add__); BIND_METHOD(bytes,__iter__); @@ -247,7 +391,27 @@ void _createAndBind_bytesClass(void) { krk_finalizeClass(bytes); KrkClass * bytesiterator = ADD_BASE_CLASS(vm.baseClasses->bytesiteratorClass, "bytesiterator", vm.baseClasses->objectClass); + bytesiterator->allocSize = sizeof(struct BytesIterator); + bytesiterator->_ongcscan = _bytesiterator_gcscan; BIND_METHOD(bytesiterator,__init__); BIND_METHOD(bytesiterator,__call__); krk_finalizeClass(bytesiterator); + + KrkClass * bytearray = ADD_BASE_CLASS(vm.baseClasses->bytearrayClass, "bytearray", vm.baseClasses->objectClass); + bytearray->allocSize = sizeof(struct ByteArray); + bytearray->_ongcscan = _bytearray_gcscan; + KRK_DOC(BIND_METHOD(bytearray,__init__), + "@brief A mutable array of bytes.\n" + "@arguments bytes=None"); + BIND_METHOD(bytearray,__repr__); + BIND_METHOD(bytearray,__len__); + BIND_METHOD(bytearray,__contains__); + BIND_METHOD(bytearray,__getitem__); + BIND_METHOD(bytearray,__setitem__); + BIND_METHOD(bytearray,__getslice__); + BIND_METHOD(bytearray,__eq__); + BIND_METHOD(bytearray,__iter__); + BIND_METHOD(bytearray,decode); + krk_defineNative(&bytearray->methods,"__str__",FUNC_NAME(bytearray,__repr__)); /* alias */ + krk_finalizeClass(bytearray); }