summaryrefslogtreecommitdiff
path: root/kernel/lib/ringbuffer.c
blob: 425971d6e5b07ec55c679a340d8f5545d02bb88d (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
#include <assert.h>
#include <kmalloc.h>
#include <lib/ringbuffer.h>
#include <math.h>
#include <string.h>

int ringbuffer_init(struct ringbuffer *rb, u32 buffer_size) {
  rb->buffer_size = 0;
  rb->read_ptr = 0;
  rb->write_ptr = 0;
  rb->buffer = kmalloc(buffer_size);
  if (!rb->buffer) {
    return 0;
  }
  rb->buffer_size = buffer_size;
  return 1;
}

u32 ringbuffer_write(struct ringbuffer *rb, const u8 *buffer, u32 len) {
  const u32 orig_len = len;
  for (; len > 0;) {
    u32 buffer_left;
    if (rb->write_ptr >= rb->read_ptr) {
      buffer_left = rb->buffer_size - rb->write_ptr;
      if (0 == rb->read_ptr && buffer_left > 0) {
        buffer_left--;
      }
    } else {
      buffer_left = (rb->read_ptr - rb->write_ptr - 1);
    }

    if (0 == buffer_left) {
      break;
    }

    u32 write_len = min(len, buffer_left);
    write_len = min(write_len, rb->buffer_size - rb->write_ptr);
    memcpy(rb->buffer + rb->write_ptr, buffer, write_len);
    buffer += write_len;
    len -= write_len;
    rb->write_ptr = (rb->write_ptr + write_len) % rb->buffer_size;
  }
  return orig_len - len;
}

u32 ringbuffer_read(struct ringbuffer *rb, u8 *buffer, u32 len) {
  const u32 orig_len = len;
  for (; len > 0;) {
    if (rb->read_ptr == rb->write_ptr) {
      break;
    }
    u32 read_len;
    if (rb->read_ptr >= rb->write_ptr) {
      read_len = rb->buffer_size - rb->read_ptr;
    } else if (rb->read_ptr < rb->write_ptr) {
      read_len = rb->write_ptr - rb->read_ptr;
    }
    read_len = min(len, read_len);

    memcpy(buffer, rb->buffer + rb->read_ptr, read_len);
    len -= read_len;
    buffer += read_len;
    rb->read_ptr = (rb->read_ptr + read_len) % rb->buffer_size;
  }
  return orig_len - len;
}

void ringbuffer_free(struct ringbuffer *rb) {
  kfree(rb->buffer);
  rb->buffer = NULL;
  rb->buffer_size = 0;
  rb->write_ptr = 0;
  rb->read_ptr = 0;
}

#ifdef KERNEL_TEST
void ringbuffer_test(void) {
  char buffer[4096];
  struct ringbuffer rb;
  assert(ringbuffer_init(&rb, 6));
  assert(2 == ringbuffer_write(&rb, "ab", 2));
  assert(2 == ringbuffer_write(&rb, "cd", 2));
  assert(1 == ringbuffer_write(&rb, "ef", 2));
  assert(0 == ringbuffer_write(&rb, "gh", 2));
  assert(2 == ringbuffer_read(&rb, buffer, 2));
  assert(0 == memcmp(buffer, "ab", 2));
  assert(2 == ringbuffer_read(&rb, buffer, 2));
  assert(0 == memcmp(buffer, "cd", 2));
  assert(1 == ringbuffer_read(&rb, buffer, 2));
  assert(0 == memcmp(buffer, "e", 1));
  assert(0 == ringbuffer_read(&rb, buffer, 2));
  ringbuffer_free(&rb);
}
#endif // KERNEL_TEST