aboutsummaryrefslogtreecommitdiff
path: root/src/stdlib/bytes.c
blob: 9492c14c3bc0b1a7f2d235226a117f41b2df703d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// The logic for unsigned bytes
#include <stdbool.h>
#include <stdint.h>

#include "bytes.h"
#include "integers.h"
#include "stdlib.h"
#include "text.h"
#include "util.h"

public const Byte_t Byteヽmin = 0;
public const Byte_t Byteヽmax = UINT8_MAX;

PUREFUNC public Text_t Byteヽas_text(const void *b, bool colorize, const TypeInfo_t *info)
{
    (void)info;
    if (!b) return Text("Byte");
    Byte_t byte = *(Byte_t*)b;
    char digits[] = {'0', 'x', 
        (byte / 16) <= 9 ? '0' + (byte / 16) : 'a' + (byte / 16) - 10,
        (byte & 15) <= 9 ? '0' + (byte & 15) : 'a' + (byte & 15) - 10,
        '\0',
    };
    Text_t text = Textヽfrom_str(digits);
    if (colorize) text = Texts(Text("\x1b[35m"), text, Text("\x1b[m"));
    return text;
}

public CONSTFUNC bool Byteヽis_between(const Byte_t x, const Byte_t low, const Byte_t high) {
    return low <= x && x <= high;
}

public OptionalByte_t Byteヽparse(Text_t text, Text_t *remainder)
{
    OptionalInt_t full_int = Intヽparse(text, remainder);
    if (full_int.small != 0L
        && Intヽcompare_value(full_int, I(0)) >= 0
        && Intヽcompare_value(full_int, I(255)) <= 0) {
        return (OptionalByte_t){.value=Byteヽfrom_int(full_int, true)};
    } else {
        return NONE_BYTE;
    }
}

public Text_t Byteヽhex(Byte_t byte, bool uppercase, bool prefix) {
    struct Text_s text = {.tag=TEXT_ASCII};
    text.ascii = GC_MALLOC_ATOMIC(8);
    char *p = (char*)text.ascii;
    if (prefix) {
        *(p++) = '0';
        *(p++) = 'x';
    }

    if (uppercase) {
        *(p++) = (byte/16) > 9 ? 'A' + (byte/16) - 10 : '0' + (byte/16);
        *(p++) = (byte & 15) > 9 ? 'A' + (byte & 15) - 10 : '0' + (byte & 15);
    } else {
        *(p++) = (byte/16) > 9 ? 'a' + (byte/16) - 10 : '0' + (byte/16);
        *(p++) = (byte & 15) > 9 ? 'a' + (byte & 15) - 10 : '0' + (byte & 15);
    }
    text.length = (int64_t)(p - text.ascii);
    return text;
}

public bool Byteヽget_bit(Byte_t x, Int_t bit_index) {
    if (Intヽcompare_value(bit_index, I(1)) < 0)
        fail("Invalid bit index (expected 1 or higher): ", bit_index);
    if (Intヽcompare_value(bit_index, I(8)) > 0)
        fail("Bit index is too large! There are only 8 bits in a byte, but index is: ", bit_index);
    return ((x & (Byte_t)(1L << (Int64ヽfrom_int(bit_index, true)-1L))) != 0);
}

#ifdef __TINYC__
#define __builtin_add_overflow(x, y, result) ({ *(result) = (x) + (y); false; })
#endif

typedef struct {
    OptionalByte_t current, last;
    Int8_t step;
} ByteRange_t;

static OptionalByte_t _next_Byte(ByteRange_t *info) {
    OptionalByte_t i = info->current;
    if (!i.is_none) {
        Byte_t next; bool overflow = __builtin_add_overflow(i.value, info->step, &next);
        if (overflow || (!info->last.is_none && (info->step >= 0 ? next > info->last.value : next < info->last.value)))
            info->current = (OptionalByte_t){.is_none=true};
        else
            info->current = (OptionalByte_t){.value=next};
    }
    return i;
}

public CONSTFUNC Closure_t Byteヽto(Byte_t first, Byte_t last, OptionalInt8_t step) {
    ByteRange_t *range = GC_MALLOC(sizeof(ByteRange_t));
    range->current = (OptionalByte_t){.value=first};
    range->last = (OptionalByte_t){.value=last};
    range->step = step.is_none ? (last >= first ? 1 : -1) : step.value;
    return (Closure_t){.fn=_next_Byte, .userdata=range};
}

public PUREFUNC Byte_t Byteヽfrom_int(Int_t i, bool truncate) {
    if unlikely (!truncate && Intヽcompare_value(i, I_small(0xFF)) > 0)
        fail("This value is too large to convert to a byte without truncation: ", i);
     else if unlikely (!truncate && Intヽcompare_value(i, I_small(0)) < 0)
        fail("Negative values can't be converted to bytes: ", i);
    return (Byte_t)(i.small >> 2);
}
public PUREFUNC Byte_t Byteヽfrom_int64(Int64_t i, bool truncate) {
    if unlikely (!truncate && i != (Int64_t)(Byte_t)i)
        fail("This value can't be converted to a byte without truncation: ", i);
    return (Byte_t)i;
}
public PUREFUNC Byte_t Byteヽfrom_int32(Int32_t i, bool truncate) {
    if unlikely (!truncate && i != (Int32_t)(Byte_t)i)
        fail("This value can't be converted to a byte without truncation: ", i);
    return (Byte_t)i;
}
public PUREFUNC Byte_t Byteヽfrom_int16(Int16_t i, bool truncate) {
    if unlikely (!truncate && i != (Int16_t)(Byte_t)i)
        fail("This value can't be converted to a byte without truncation: ", i);
    return (Byte_t)i;
}

public const TypeInfo_t Byteヽinfo = {
    .size=sizeof(Byte_t),
    .align=__alignof__(Byte_t),
    .metamethods={
        .as_text=Byteヽas_text,
    },
};

// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0