common: Add the buffer module.
[gps-watch.git] / src / common / buffer.rs
diff --git a/src/common/buffer.rs b/src/common/buffer.rs
new file mode 100644 (file)
index 0000000..deb8ef7
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2017-2019 Tilman Sauerbeck (tilman at code-monkey de)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+use core::ptr;
+
+pub enum BufferUserData {
+}
+
+#[repr(C)]
+pub struct Buffer {
+    buf: *mut u8,
+    callback: extern fn(*mut BufferUserData, *const u8, usize, usize) -> isize,
+    user_data: *mut BufferUserData,
+    allocated: usize,
+    length: usize,
+}
+
+extern fn dummy_callback(_: *mut BufferUserData, _: *const u8, _: usize, _: usize) -> isize {
+    -1
+}
+
+#[no_mangle]
+pub unsafe extern fn buffer_init(_buffer: *mut Buffer, buf: *mut u8, bufsiz: usize, callback: extern fn(*mut BufferUserData, *const u8, usize, usize) -> isize, user_data: *mut BufferUserData) {
+    let buffer = &mut *_buffer;
+
+    buffer.buf = buf;
+    buffer.allocated = bufsiz;
+    buffer.callback = callback;
+    buffer.user_data = user_data;
+    buffer.length = 0;
+}
+
+unsafe fn buffer_consume(buffer: &mut Buffer, length: usize) {
+    buffer.length -= length;
+
+    ptr::copy(buffer.buf.offset(length as isize), buffer.buf, buffer.length);
+}
+
+#[no_mangle]
+pub unsafe extern fn buffer_flush(_buffer: *mut Buffer) -> i32 {
+    let buffer = &mut *_buffer;
+
+    while buffer.length > 0 {
+        let nwritten = (buffer.callback)(buffer.user_data, buffer.buf, buffer.allocated, buffer.length);
+
+        if nwritten == -1 {
+            return -1;
+        }
+
+        buffer_consume(buffer, nwritten as usize);
+    }
+
+    0
+}
+
+fn min(a: usize, b: usize) -> usize {
+    if a < b {
+        a
+    } else {
+        b
+    }
+}
+
+#[no_mangle]
+pub unsafe extern fn buffer_write(_buffer: *mut Buffer, src: *const u8, srcsiz: usize) -> isize {
+    let buffer = &mut *_buffer;
+
+    if buffer.allocated == buffer.length {
+        if buffer_flush(_buffer) < 0 {
+            return -1;
+        }
+    }
+
+    let nwrite = min(buffer.allocated - buffer.length, srcsiz);
+
+    ptr::copy_nonoverlapping(src, buffer.buf.offset(buffer.length as isize), nwrite);
+
+    buffer.length += nwrite;
+
+    nwrite as isize
+}
+
+#[no_mangle]
+pub unsafe extern fn buffer_write_all(_buffer: *mut Buffer, src: *const u8, srcsiz: usize) -> isize {
+    let mut offset = 0;
+    let mut left = srcsiz;
+
+    while left > 0 {
+        let data = src.offset(offset);
+        let nwritten = buffer_write(_buffer, data, left);
+
+        if nwritten < 0 {
+            return -1;
+        }
+
+        left -= nwritten as usize;
+        offset += nwritten;
+    }
+
+    0
+}
+
+impl Buffer {
+    // Allocates and initializes a buffer.
+    pub fn new(buf: *mut u8, bufsiz: usize, callback: extern fn(*mut BufferUserData, *const u8, usize, usize) -> isize, user_data: *mut BufferUserData) -> Buffer {
+        Buffer {
+            buf: buf,
+            callback: callback,
+            user_data: user_data,
+            allocated: bufsiz,
+            length: 0,
+        }
+    }
+
+    // Allocates a buffer, but does not initialize it.
+    // The buffer won't be usable until the init() function is called on it.
+    pub fn alloc() -> Buffer {
+        Buffer {
+            buf: 0 as *mut u8,
+            callback: dummy_callback,
+            user_data: 0 as *mut BufferUserData,
+            allocated: 0,
+            length: 0,
+        }
+    }
+
+    pub fn init(&mut self, buf: *mut u8, bufsiz: usize, callback: extern fn(*mut BufferUserData, *const u8, usize, usize) -> isize, user_data: *mut BufferUserData) {
+        unsafe {
+            buffer_init(self, buf, bufsiz, callback, user_data);
+        }
+    }
+
+    pub fn write(&mut self, data: &[u8]) {
+        unsafe {
+            buffer_write_all(self, data.as_ptr(), data.len());
+        }
+    }
+
+    pub fn flush(&mut self) -> i32 {
+        unsafe {
+            buffer_flush(self)
+        }
+    }
+
+    pub fn len(&self) -> usize {
+        self.length
+    }
+}