aboutsummaryrefslogtreecommitdiff
path: root/buf.c
blob: b3150a41cec22057e045afafd97aafbdb15517e5 (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
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "buf.h"

int min(int a, int b) {
    return ((a<b) ? a : b);
}

struct buf_node{
    char* text;
    struct buf_node* next;
};

struct buf{
    buf_node* head;
    buf_node* tail;
    size_t cap;
    size_t ins;
    size_t pop;
}; // a "thick" linked list (buffered for mem efficiency)

static buf_node* newbufnode(size_t cap){
    buf_node* bufnode = malloc(sizeof(buf_node));
    bufnode->text = malloc(sizeof(char)*cap);
    return bufnode;
}

buf* newbuf(size_t cap){
    buf* buffer = malloc(sizeof(buf));
    buffer->tail = buffer->head = newbufnode(cap);
    buffer->cap = cap;
    buffer->ins = buffer->pop = 0;
    return buffer;
}

size_t buflen(buf* buffer){
    buf_node* cur = buffer->tail;
    size_t size = buffer->ins-buffer->pop;
    while (cur != buffer->head) {
        cur = cur->next;
        size += buffer->cap;
    };
    return size;
}

static void extendbuf(buf* buffer){
    buffer->head->next = newbufnode(buffer->cap);
    buffer->head = buffer->head->next;
    // buffer->ins = 0 is taken care of in the function that uses this.
}

static void shortenbuf(buf* buffer){
    buf_node* tmp = buffer->tail;
    buffer->tail = buffer->tail->next;
    free(tmp->text);
    free(tmp);
}

char popchrbuf(buf* buffer){
    if (buffer->pop == buffer->cap){
        shortenbuf(buffer);
        buffer->pop = 0;
    }
    buffer->pop++;
    return buffer->tail->text[buffer->pop-1];
}
char* popstrbuf(buf* buffer, size_t ct){
    char* str = malloc((ct+1)*sizeof(char)); str[ct] = 0;
    memcpy(str, buffer->tail->text+buffer->pop,
        min(ct, buffer->cap-buffer->pop));
    size_t loc;
    for (loc = buffer->cap - buffer->pop; loc < ct; loc += buffer->cap){
        shortenbuf(buffer);
        memcpy(str+loc, buffer->tail->text, min(ct-loc, buffer->cap));
    }
    buffer->pop = ct - loc + buffer->cap;
    return str;
}

void inschrbuf(buf* buffer, char chr){
    if (buffer->ins == buffer->cap){
        extendbuf(buffer);
        buffer->ins = 0;
    }
    buffer->ins++;
    buffer->head->text[buffer->ins-1] = chr;
}
void insstrbuf(buf* buffer, char* str){
    size_t len = strlen(str);
    size_t tgt = min(len, buffer->cap - buffer->ins);
    memcpy(buffer->head->text+buffer->ins, str, tgt);
    for (;tgt<len; tgt += buffer->cap){
        extendbuf(buffer);
        memcpy(buffer->head->text, str+tgt, min(len-tgt, buffer->cap));
    }
    buffer->ins = len - tgt + buffer->cap;
}

char* peekstrbuf(buf* buffer, size_t loc, size_t len){
    char* str = malloc(sizeof(char)*(len+1)); str[len] = 0;
    buf_node* start;

    loc += buffer->pop;
    for (start = buffer->tail; loc>buffer->cap; loc -= buffer->cap)
        start = start->next; // start,loc is where one starts streaming
    size_t bytect;
    for (size_t tgt = 0; len > 0; len -= bytect, tgt += bytect){
        bytect = min(buffer->cap - loc, len);
        memcpy(str+tgt, start->text+loc, bytect);
        start = start->next;
        loc = 0;
    }
    return str;
}
char peekchrbuf(buf* buffer, size_t loc){
    buf_node* start;
    loc += buffer->pop;
    for (start = buffer->tail; loc>buffer->cap; loc -= buffer->cap)
        start = start->next;
    return start->text[loc];
}