summaryrefslogtreecommitdiff
path: root/source/kit/input_buffer.c
blob: c1ae748630f68123d2b79a8f3c24ea1bab233819 (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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include "input_buffer.h"

#include <assert.h>
#include <string.h>

typedef struct {
  i64               ref_count;
  kit_is_handle_t   upstream;
  kit_allocator_t  *alloc;
  kit_str_builder_t data;
} internal_buffer_t;

static internal_buffer_t *kit_buf_init_(kit_is_handle_t  upstream,
                                        kit_allocator_t *alloc) {
  internal_buffer_t *buf = kit_alloc_dispatch(alloc, KIT_ALLOCATE,
                                              sizeof *buf, 0, NULL);

  if (buf != NULL) {
    memset(buf, 0, sizeof *buf);
    buf->ref_count = 1;
    buf->upstream  = upstream;
    buf->alloc     = alloc;
    DA_INIT(buf->data, 0, alloc);
  }

  return buf;
}

static kit_allocator_t *kit_buf_alloc_(void *p) {
  assert(p != NULL);

  return ((internal_buffer_t *) p)->alloc;
}

static void kit_buf_acquire_(void *p) {
  assert(p != NULL);

  ((internal_buffer_t *) p)->ref_count++;
}

static void kit_buf_release_(void *p) {
  assert(p != NULL);

  internal_buffer_t *buf = (internal_buffer_t *) p;

  if (--buf->ref_count == 0) {
    DA_DESTROY(buf->data);
    kit_alloc_dispatch(buf->alloc, KIT_DEALLOCATE, 0, 0, buf);
  }
}

static void kit_buf_adjust_(void *p, i64 size) {
  assert(p != NULL);
  assert(size >= 0);

  internal_buffer_t *buf    = (internal_buffer_t *) p;
  i64                offset = buf->data.size;

  if (offset < size) {
    DA_RESIZE(buf->data, size);
    kit_str_t destination = { .size   = size - offset,
                              .values = buf->data.values + offset };
    i64       n           = KIT_IS_READ(buf->upstream, destination);
    DA_RESIZE(buf->data, offset + n);
  }
}

static i64 kit_buf_read_(void *p, i64 offset, kit_str_t destination) {
  internal_buffer_t *buf = (internal_buffer_t *) p;
  i64                n   = destination.size < buf->data.size - offset
                               ? destination.size
                               : buf->data.size - offset;
  memcpy(destination.values, buf->data.values + offset, n);
  return n;
}

kit_ib_handle_t kit_ib_wrap(kit_is_handle_t  upstream,
                            kit_allocator_t *alloc) {
  kit_ib_handle_t buf;
  memset(&buf, 0, sizeof buf);
  buf.status = KIT_OK;
  DA_INIT(buf.data, 0, alloc);
  buf.internal = kit_buf_init_(upstream, alloc);
  if (buf.internal == NULL)
    buf.status = KIT_ERROR_BAD_ALLOC;
  return buf;
}

kit_ib_handle_t kit_ib_read(kit_ib_handle_t buf, i64 size) {
  kit_ib_handle_t next;
  memset(&next, 0, sizeof next);

  if (buf.status != KIT_OK) {
    next.status = buf.status;
    return next;
  }

  kit_buf_acquire_(buf.internal);
  kit_buf_adjust_(buf.internal, buf.offset + size);

  DA_INIT(next.data, size, kit_buf_alloc_(buf.internal));
  if (next.data.size != size)
    next.status = KIT_ERROR_BAD_ALLOC;

  kit_str_t destination = { .size   = next.data.size,
                            .values = next.data.values };
  i64       n = kit_buf_read_(buf.internal, buf.offset, destination);
  next.offset = buf.offset + n;
  next.internal = buf.internal;

  DA_RESIZE(next.data, n);
  if (next.data.size != n)
    next.status = KIT_ERROR_BAD_ALLOC;

  return next;
}

kit_ib_handle_t kit_ib_read_while(
    kit_ib_handle_t buf, kit_ib_read_condition_fn condition) {
  kit_ib_handle_t next;
  memset(&next, 0, sizeof next);

  if (buf.status != KIT_OK) {
    next.status = buf.status;
    return next;
  }

  kit_buf_acquire_(buf.internal);

  DA_INIT(next.data, 0, kit_buf_alloc_(buf.internal));

  i64 size = 0;

  for (;; ++size) {
    kit_buf_adjust_(buf.internal, buf.offset + size + 1);

    DA_RESIZE(next.data, size + 1);
    if (next.data.size != size + 1)
      next.status = KIT_ERROR_BAD_ALLOC;

    kit_str_t destination = { .size   = 1,
                              .values = next.data.values + size };
    i64       n = kit_buf_read_(buf.internal, buf.offset + size,
                                destination);

    kit_str_t data = { .size = size + 1, .values = next.data.values };
    if (n != 1 || condition == NULL || condition(data) == 0)
      break;
  }

  next.offset   = buf.offset + size;
  next.internal = buf.internal;

  DA_RESIZE(next.data, size);
  if (next.data.size != size)
    next.status = KIT_ERROR_BAD_ALLOC;

  return next;
}

void kit_ib_destroy(kit_ib_handle_t buf) {
  kit_buf_release_(buf.internal);
  DA_DESTROY(buf.data);
}