Browse Source

Extended bytes, bytesiterator; added bytearray

K. Lange 1 year ago
parent
commit
b4887a78b0
3 changed files with 189 additions and 24 deletions
  1. 2 2
      src/kuroko/util.h
  2. 1 0
      src/kuroko/vm.h
  3. 186 22
      src/obj_bytes.c

+ 2 - 2
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));

+ 1 - 0
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 */
 };
 
 /**

+ 186 - 22
src/obj_bytes.c

@@ -4,6 +4,11 @@
 #include <kuroko/memory.h>
 #include <kuroko/util.h>
 
+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__,{
+	KrkValue _list = self->l;
+	size_t _counter = self->i;
+	if (_counter >= AS_BYTES(_list)->length) {
+		return argv[0];
+	} else {
+		self->i = _counter + 1;
+		return INTEGER_VAL(AS_BYTES(_list)->bytes[_counter]);
+	}
+})
+
+#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();
-	KrkValue _bytes;
-	KrkValue _counter;
-	const char * errorStr = NULL;
-	if (!krk_tableGet(&self->fields, OBJECT_VAL(S("s")), &_bytes)) {
-		errorStr = "no str pointer";
-		goto _corrupt;
+	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);
 	}
-	if (!krk_tableGet(&self->fields, OBJECT_VAL(S("i")), &_counter)) {
-		errorStr = "no index";
-		goto _corrupt;
+
+	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;
 
-	if ((size_t)AS_INTEGER(_counter) >= AS_BYTES(_bytes)->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)]);
+	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);
 	}
-_corrupt:
-	return krk_runtimeError(vm.exceptions->typeError, "Corrupt bytes iterator: %s", errorStr);
+	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);
 }