Source

arana-main / arana / string.c

/*
 * string.c
 *
 * (c) Copyright 2009 by Armin Ronacher.
 */

#include "arana.h"


AR
arana_string_new(AR_SIG, const char *value, Ar_size_t size, int nocopy)
{
    ArString *self = (ArString *)AR_ALLOCATE_OBJECT(ArString, ArStringType);
    if (size == 0 && value)
        size = strlen(value);
    self->size = size;
    if (nocopy)
        self->value = value;
    else {
        char *copy = AR_ALLOC(size + 1);
        if (!copy)
            AR_RAISE_OOM();
        memcpy(copy, value, size);
        copy[size] = '\0';
        self->value = copy;
    }
    return (AR)self;
}

/* invoked by AR_STRING_CONCAT */
AR
_arana_api_string_concat(AR_SIG, AR self, AR other)
{
    if (!AR_IS_STRING(other))
        other = AR_TO_STRING(other);
    Ar_size_t length = AR_STRING_SIZE(self) + AR_STRING_SIZE(other);
    char *result = AR_ALLOC(length + 1);
    if (!result)
        AR_RAISE_OOM();
    memcpy(result, AR_AS_CHARP(self), AR_STRING_SIZE(self));
    memcpy(result + AR_STRING_SIZE(self), AR_AS_CHARP(other),
           AR_STRING_SIZE(other) + 1);
    return AR_STRING2_NOCOPY(result, length);
}

static AR
arana_string_concat(AR_SIG, AR self, AR args)
{
    AR other;
    AR_PARSE(args, "O::concat", &other);
    return AR_STRING_CONCAT(self, other);
}

static AR
arana_string_mul(AR_SIG, AR self, AR args)
{
    AR other;
    AR_PARSE(args, "I::mul", &other);
    unsigned long times = AR_AS_LONG(other);
    if (times < 0)
        times = 0;
    Ar_size_t size = AR_STRING_SIZE(self);
    Ar_size_t totalsize = size * times;
    char *result = AR_ALLOC(totalsize + 1);
    char *cur = result;
    if (!result)
        AR_RAISE_OOM();
    for (int i = 0; i < times; ++i) {
        memcpy(cur, AR_AS_CHARP(self), size);
        cur += size;
    }
    return AR_STRING2_NOCOPY(result, totalsize);
}

static AR
arana_string_length_get(AR_SIG, AR self, AR args)
{
    AR_ASSERT_EMPTY_ARGS(args, "length:get");
    return AR_INTEGER(AR_STRING_SIZE(self));
}

static AR
arana_string_to_string(AR_SIG, AR self, AR args)
{
    AR_ASSERT_EMPTY_ARGS(args, "to_string");
    return self;
}

static AR
arana_string_to_repr(AR_SIG, AR self, AR args)
{
    Ar_size_t true_length = AR_STRING_SIZE(self);
    Ar_size_t chars_left = true_length;
    Ar_size_t new_length = true_length + 2;
    const char *in = AR_AS_CHARP(self);
    char *result, *out;

    AR_ASSERT_EMPTY_ARGS(args, "to_repr");

    while (chars_left--) {
        if (*in == '\a' || *in == '\b' || *in == '\f' ||
            *in == '\n' || *in == '\r' || *in == '\t' ||
            *in == '\v' || *in == '\\' || *in == '"'  || *in == '#')
            new_length++;
        else if (*in < ' ' || *in > '~')
            new_length += 3;
        in++;
    }

    result = out = AR_ALLOC(new_length + 1);
    in = AR_AS_CHARP(self);
    *out++ = '"';
    for (chars_left = true_length; chars_left--; in++) {
        if (*in == '\\' || *in == '"' || *in == '#')
            *out++ = '\\', *out++ = *in;
        else if (*in == '\a')
            *out++ = '\\', *out++ = 'a';
        else if (*in == '\b')
            *out++ = '\\', *out++ = 'b';
        else if (*in == '\f')
            *out++ = '\\', *out++ = 'f';
        else if (*in == '\n')
            *out++ = '\\', *out++ = 'n';
        else if (*in == '\r')
            *out++ = '\\', *out++ = 'r';
        else if (*in == '\t')
            *out++ = '\\', *out++ = 't';
        else if (*in == '\v')
            *out++ = '\\', *out++ = 'v';
        else if (*in < ' ' || *in > '~')
            sprintf(out, "\\x%02x", *in & 0xff), out += 4;
        else
            *out++ = *in;
    }
    *out++ = '"';

    return AR_STRING2_NOCOPY(result, new_length);
}

static AR
arana_string_get_iterator(AR_SIG, AR self, AR args)
{
    ArStringIterator *iterator;
    AR_ASSERT_EMPTY_ARGS(args, "get_iterator");
    iterator = (ArStringIterator *)
        AR_ALLOCATE_OBJECT(ArStringIterator, ArStringIteratorType);
    iterator->pos = 0;
    iterator->string = self;
    return (AR)iterator;
}

static AR
arana_string_iterator_next(AR_SIG, AR self, AR args)
{
    ArStringIterator *iter = (ArStringIterator *)self;
    AR_ASSERT_EMPTY_ARGS(args, "next");
    if (iter->pos >= AR_STRING_SIZE(iter->string))
        AR_RAISE(AR_STOP_ITERATION());
    return AR_STRING2(
        (AR_AS_CHARP(iter->string) + iter->pos++), 1);
}

ArObjectType ArStringType = {
    .name = "String",
    .parent = &ArTypeType
};

ArObjectType ArStringIteratorType = {
    .name = "StringIterator",
    .parent = &ArTypeType
};

AR
arana_create_string_type(AR_SIG)
{
    AR type = AR_CREATE_TYPE(ArStringType);
    AR_BIND_METHOD(type, ":concat", arana_string_concat);
    AR_BIND_METHOD(type, ":mul", arana_string_mul);
    AR_BIND_METHOD(type, "length:get", arana_string_length_get);
    AR_BIND_METHOD(type, "to_string", arana_string_to_string);
    AR_BIND_METHOD(type, "to_repr", arana_string_to_repr);
    AR_BIND_METHOD(type, "get_iterator", arana_string_get_iterator);
    return type;
}

AR
arana_create_string_iterator_type(AR_SIG)
{
    AR type = AR_CREATE_TYPE(ArStringIteratorType);
    AR_BIND_METHOD(type, "next", arana_string_iterator_next);
    AR_BIND_METHOD(type, "get_iterator", arana_generic_get_self_iterator);
    return type;
}