useful methods for bytes

This commit is contained in:
K. Lange 2021-02-14 11:18:54 +09:00
parent e0adfcdc80
commit 22453f42e6
2 changed files with 119 additions and 58 deletions

View File

@ -4,11 +4,18 @@
#include "memory.h"
#include "util.h"
static KrkValue _bytes_init(int argc, KrkValue argv[], int hasKw) {
if (argc == 1) {
#define AS_bytes(o) AS_BYTES(o)
#define CURRENT_CTYPE KrkBytes *
#define CURRENT_NAME self
#define IS_bytes(o) (IS_BYTES(o) || krk_isInstanceOf(o, vm.baseClasses->bytesClass))
KRK_METHOD(bytes,__init__,{
if (argc < 2) {
return OBJECT_VAL(krk_newBytes(0,NULL));
}
METHOD_TAKES_AT_MOST(1);
/* TODO: Use generic unpacker */
if (IS_TUPLE(argv[1])) {
KrkBytes * out = krk_newBytes(AS_TUPLE(argv[1])->values.count, NULL);
krk_push(OBJECT_VAL(out));
@ -20,13 +27,27 @@ static KrkValue _bytes_init(int argc, KrkValue argv[], int hasKw) {
}
krk_bytesUpdateHash(out);
return krk_pop();
} else if (IS_list(argv[1])) {
KrkBytes * out = krk_newBytes(AS_LIST(argv[1])->count, NULL);
krk_push(OBJECT_VAL(out));
for (size_t i = 0; i < AS_LIST(argv[1])->count; ++i) {
if (!IS_INTEGER(AS_LIST(argv[1])->values[i])) {
return krk_runtimeError(vm.exceptions->typeError, "bytes(): expected list of ints, not of '%s'", krk_typeName(AS_LIST(argv[1])->values[i]));
}
out->bytes[i] = AS_INTEGER(AS_LIST(argv[1])->values[i]);
}
krk_bytesUpdateHash(out);
return krk_pop();
}
return krk_runtimeError(vm.exceptions->typeError, "Can not convert '%s' to bytes", krk_typeName(argv[1]));
}
})
#undef IS_bytes
#define IS_bytes(o) IS_BYTES(o)
/* bytes objects are not interned; need to do this the old-fashioned way. */
static KrkValue _bytes_eq(int argc, KrkValue argv[], int hasKw) {
KRK_METHOD(bytes,__eq__,{
if (!IS_BYTES(argv[1])) return BOOLEAN_VAL(0);
KrkBytes * self = AS_BYTES(argv[0]);
KrkBytes * them = AS_BYTES(argv[1]);
@ -36,61 +57,52 @@ static KrkValue _bytes_eq(int argc, KrkValue argv[], int hasKw) {
if (self->bytes[i] != them->bytes[i]) return BOOLEAN_VAL(0);
}
return BOOLEAN_VAL(1);
}
})
#define PUSH_CHAR(c) do { if (stringCapacity < stringLength + 1) { \
size_t old = stringCapacity; stringCapacity = GROW_CAPACITY(old); \
stringBytes = GROW_ARRAY(char, stringBytes, old, stringCapacity); \
} stringBytes[stringLength++] = c; } while (0)
#define AT_END() (self->length == 0 || i == self->length - 1)
static KrkValue _bytes_repr(int argc, KrkValue argv[], int hasKw) {
size_t stringCapacity = 0;
size_t stringLength = 0;
char * stringBytes = NULL;
KRK_METHOD(bytes,__repr__,{
struct StringBuilder sb = {0};
PUSH_CHAR('b');
PUSH_CHAR('\'');
pushStringBuilder(&sb, 'b');
pushStringBuilder(&sb, '\'');
for (size_t i = 0; i < AS_BYTES(argv[0])->length; ++i) {
uint8_t ch = AS_BYTES(argv[0])->bytes[i];
switch (ch) {
case '\\': PUSH_CHAR('\\'); PUSH_CHAR('\\'); break;
case '\'': PUSH_CHAR('\\'); PUSH_CHAR('\''); break;
case '\a': PUSH_CHAR('\\'); PUSH_CHAR('a'); break;
case '\b': PUSH_CHAR('\\'); PUSH_CHAR('b'); break;
case '\f': PUSH_CHAR('\\'); PUSH_CHAR('f'); break;
case '\n': PUSH_CHAR('\\'); PUSH_CHAR('n'); break;
case '\r': PUSH_CHAR('\\'); PUSH_CHAR('r'); break;
case '\t': PUSH_CHAR('\\'); PUSH_CHAR('t'); break;
case '\v': PUSH_CHAR('\\'); PUSH_CHAR('v'); break;
case '\\': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, '\\'); break;
case '\'': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, '\''); break;
case '\a': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'a'); break;
case '\b': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'b'); break;
case '\f': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'f'); break;
case '\n': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'n'); break;
case '\r': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'r'); break;
case '\t': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 't'); break;
case '\v': pushStringBuilder(&sb, '\\'); pushStringBuilder(&sb, 'v'); break;
default: {
if (ch < ' ' || ch >= 0x7F) {
PUSH_CHAR('\\');
PUSH_CHAR('x');
pushStringBuilder(&sb, '\\');
pushStringBuilder(&sb, 'x');
char hex[3];
sprintf(hex,"%02x", ch);
PUSH_CHAR(hex[0]);
PUSH_CHAR(hex[1]);
pushStringBuilder(&sb, hex[0]);
pushStringBuilder(&sb, hex[1]);
} else {
PUSH_CHAR(ch);
pushStringBuilder(&sb, ch);
}
break;
}
}
}
PUSH_CHAR('\'');
pushStringBuilder(&sb, '\'');
KrkValue tmp = OBJECT_VAL(krk_copyString(stringBytes, stringLength));
if (stringBytes) FREE_ARRAY(char,stringBytes,stringCapacity);
return tmp;
}
return finishStringBuilder(&sb);
})
static KrkValue _bytes_get(int argc, KrkValue argv[], int hasKw) {
if (argc < 2) return krk_runtimeError(vm.exceptions->argumentError, "bytes.__get__(): expected one argument");
KrkBytes * self = AS_BYTES(argv[0]);
long asInt = AS_INTEGER(argv[1]);
KRK_METHOD(bytes,__get__,{
METHOD_TAKES_EXACTLY(1);
CHECK_ARG(1,int,krk_integer_type,asInt);
if (asInt < 0) asInt += (long)self->length;
if (asInt < 0 || asInt >= (long)self->length) {
@ -98,34 +110,77 @@ static KrkValue _bytes_get(int argc, KrkValue argv[], int hasKw) {
}
return INTEGER_VAL(self->bytes[asInt]);
}
})
static KrkValue _bytes_len(int argc, KrkValue argv[], int hasKw) {
KRK_METHOD(bytes,__len__,{
return INTEGER_VAL(AS_BYTES(argv[0])->length);
}
})
static KrkValue _bytes_contains(int argc, KrkValue argv[], int hasKw) {
if (argc < 2) krk_runtimeError(vm.exceptions->argumentError, "bytes.__contains__(): expected one argument");
KRK_METHOD(bytes,__contains__,{
METHOD_TAKES_EXACTLY(1);
return krk_runtimeError(vm.exceptions->notImplementedError, "not implemented");
}
})
static KrkValue _bytes_decode(int argc, KrkValue argv[], int hasKw) {
/* TODO: Actually bother checking if this explodes, or support other encodings... */
KRK_METHOD(bytes,decode,{
METHOD_TAKES_NONE();
return OBJECT_VAL(krk_copyString((char*)AS_BYTES(argv[0])->bytes, AS_BYTES(argv[0])->length));
}
})
#undef PUSH_CHAR
KRK_METHOD(bytes,join,{
METHOD_TAKES_EXACTLY(1);
CHECK_ARG(1,list,KrkList*,iterable);
const char * errorStr = NULL;
struct StringBuilder sb = {0};
for (size_t i = 0; i < iterable->values.count; ++i) {
KrkValue value = iterable->values.values[i];
if (!IS_BYTES(iterable->values.values[i])) {
errorStr = krk_typeName(value);
goto _expectedBytes;
}
krk_push(value);
if (i > 0) {
for (size_t j = 0; j < self->length; ++j) {
pushStringBuilder(&sb, self->bytes[j]);
}
}
for (size_t j = 0; j < AS_STRING(value)->length; ++j) {
pushStringBuilder(&sb, AS_BYTES(value)->bytes[j]);
}
krk_pop();
}
return finishStringBuilderBytes(&sb);
_expectedBytes:
krk_runtimeError(vm.exceptions->typeError, "Expected bytes, got %s.", errorStr);
discardStringBuilder(&sb);
})
KRK_METHOD(bytes,__add__,{
METHOD_TAKES_EXACTLY(1);
CHECK_ARG(1,bytes,KrkBytes*,them);
struct StringBuilder sb = {0};
pushStringBuilderStr(&sb, (char*)self->bytes, self->length);
pushStringBuilderStr(&sb, (char*)them->bytes, them->length);
return finishStringBuilderBytes(&sb);
})
_noexport
void _createAndBind_bytesClass(void) {
ADD_BASE_CLASS(vm.baseClasses->bytesClass, "bytes", vm.baseClasses->objectClass);
krk_defineNative(&vm.baseClasses->bytesClass->methods, ".__init__", _bytes_init);
krk_defineNative(&vm.baseClasses->bytesClass->methods, ".__str__", _bytes_repr);
krk_defineNative(&vm.baseClasses->bytesClass->methods, ".__repr__", _bytes_repr);
krk_defineNative(&vm.baseClasses->bytesClass->methods, ".decode", _bytes_decode);
krk_defineNative(&vm.baseClasses->bytesClass->methods, ".__len__", _bytes_len);
krk_defineNative(&vm.baseClasses->bytesClass->methods, ".__contains__", _bytes_contains);
krk_defineNative(&vm.baseClasses->bytesClass->methods, ".__get__", _bytes_get);
krk_defineNative(&vm.baseClasses->bytesClass->methods, ".__eq__", _bytes_eq);
krk_finalizeClass(vm.baseClasses->bytesClass);
KrkClass * bytes = ADD_BASE_CLASS(vm.baseClasses->bytesClass, "bytes", vm.baseClasses->objectClass);
BIND_METHOD(bytes,__init__);
BIND_METHOD(bytes,__repr__);
BIND_METHOD(bytes,__len__);
BIND_METHOD(bytes,__contains__);
BIND_METHOD(bytes,__get__);
BIND_METHOD(bytes,__eq__);
BIND_METHOD(bytes,__add__);
BIND_METHOD(bytes,decode);
BIND_METHOD(bytes,join);
krk_defineNative(&bytes->methods,".__str__",FUNC_NAME(bytes,__repr__)); /* alias */
krk_finalizeClass(bytes);
}

View File

@ -127,6 +127,12 @@ static inline KrkValue finishStringBuilder(struct StringBuilder * sb) {
return out;
}
static inline KrkValue finishStringBuilderBytes(struct StringBuilder * sb) {
KrkValue out = OBJECT_VAL(krk_newBytes(sb->length, (uint8_t*)sb->bytes));
FREE_ARRAY(char,sb->bytes, sb->capacity);
return out;
}
static inline KrkValue discardStringBuilder(struct StringBuilder * sb) {
FREE_ARRAY(char,sb->bytes, sb->capacity);
return NONE_VAL();