gap_buffer/
lib.rs

1// Copyright 2017 Nathan Sizemore <nathanrsizemore@gmail.com>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public License,
4// v. 2.0. If a copy of the MPL was not distributed with this file, you can
5// obtain one at http://mozilla.org/MPL/2.0/.
6
7
8extern crate libc;
9
10
11use std::{fmt, mem};
12use std::ops::{Drop, Range};
13
14
15const CHUNK_SIZE: isize = 32;
16
17
18/// Dynamic array that allows efficient insertion and removal operations
19/// that are near the same location. Ideal for text editors.
20pub struct GapBuffer {
21    buf_start: *mut u8,
22    gap_start: *mut u8,
23    gap_end: *mut u8,
24    buf_end: *mut u8
25}
26
27impl GapBuffer {
28    /// Inserts `s` into the buffer at `offset`.
29    pub fn insert_str(&mut self, offset: usize, s: &str) {
30        let s_len = s.len() as isize;
31        if s_len > self.gap_len() {
32            self.grow_gap(s_len);
33        }
34
35        self.move_gap_to(offset as isize);
36
37        let src_ptr = s.as_bytes().as_ptr();
38        unsafe {
39            libc::memcpy(self.gap_start as *mut libc::c_void,
40                         src_ptr as *const libc::c_void,
41                         s_len as usize);
42            self.gap_start = self.gap_start.offset(s_len);
43        }
44    }
45
46    /// Removes `range` from the buffer.
47    pub fn remove(&mut self, range: Range<usize>) {
48        let buf_len = self.buf_len() as usize;
49        assert!(range.start < range.end, "Invalid range: {:?}", range);
50        assert!(range.start < buf_len);
51        assert!(range.end <= buf_len);
52
53        let s = self.to_string();
54        let head = &s[0..range.start];
55        let tail = &s[range.end..];
56
57        self.clear();
58        self.insert_str(0, &head);
59        self.insert_str(head.len(), &tail);
60    }
61
62    /// Creates a new buffer with a `capacity` sized allocation.
63    ///
64    /// # Panics
65    ///
66    /// * If `malloc` returns `NULL`.
67    pub fn with_capacity(capacity: usize) -> GapBuffer {
68        let buffer = unsafe {
69            let size = mem::size_of::<u8>() * capacity;
70            libc::malloc(size) as *mut u8
71        };
72
73        // malloc will return NULL if called with zero.
74        if buffer.is_null() && capacity != 0 {
75            panic!("Unable to allocate requested capacity");
76        }
77
78        GapBuffer {
79            buf_start: buffer,
80            gap_start: buffer,
81            gap_end: unsafe { buffer.offset(capacity as isize) },
82            buf_end: unsafe { buffer.offset(capacity as isize) }
83        }
84    }
85
86    fn allocate_extra(&mut self, extra: isize) {
87        let current_size = ptr_diff(self.buf_end, self.buf_start);
88        let new_size = mem::size_of::<u8>()
89            * extra as usize
90            + current_size as usize;
91
92        let new_buf = unsafe {
93            libc::realloc(self.buf_start as *mut libc::c_void,
94                          new_size) as *mut u8
95        };
96
97        assert!(!new_buf.is_null(), "Out of memory");
98
99        self.buf_start = new_buf;
100    }
101
102    fn buf_len(&self) -> isize {
103        let head_len = ptr_diff(self.gap_start, self.buf_start);
104        let tail_len = ptr_diff(self.buf_end, self.gap_end);
105        head_len + tail_len
106    }
107
108    fn clear(&mut self) {
109        self.gap_start = self.buf_start;
110        self.gap_end = self.buf_end;
111    }
112
113    fn gap_len(&self) -> isize {
114        ptr_diff(self.gap_end, self.gap_start)
115    }
116
117    fn grow_gap(&mut self, size: isize) {
118        let available = self.gap_len();
119        let needed = size - available;
120
121        let mut chunk = (needed as f32 / CHUNK_SIZE as f32).ceil() as isize;
122        chunk *= CHUNK_SIZE;
123
124        let head_len = ptr_diff(self.gap_start, self.buf_start);
125        let tail_len = ptr_diff(self.buf_end, self.gap_end);
126        let new_gap_size = self.gap_len() + chunk;
127        let buf_len = head_len + tail_len;
128
129        self.allocate_extra(chunk);
130        unsafe {
131            libc::memmove(self.gap_start as *mut libc::c_void,
132                          self.gap_end as *const libc::c_void,
133                          tail_len as usize);
134            self.gap_start = self.buf_start.offset(buf_len);
135            self.gap_end = self.gap_start.offset(new_gap_size);
136            self.buf_end = self.gap_end;
137        }
138    }
139
140    fn head(&self) -> String {
141        let head_len = ptr_diff(self.gap_start, self.buf_start) as usize;
142        string_from_segment(self.buf_start, head_len)
143    }
144
145    fn move_gap_to(&mut self, offset: isize) {
146        let gap_len = self.gap_len();
147        let new_pos = unsafe { self.buf_start.offset(offset) };
148
149        let diff = ptr_diff(new_pos, self.gap_start);
150
151        if diff == 0 { return; }
152
153        if diff < 0 {
154            unsafe {
155                self.gap_start = new_pos;
156                self.gap_end = self.gap_start.offset(gap_len);
157                libc::memmove(self.gap_end as *mut libc::c_void,
158                              self.gap_start as *mut libc::c_void,
159                              diff.abs() as usize);
160            }
161        } else {
162            unsafe {
163                self.gap_end = self.gap_end.offset(diff);
164                self.gap_start = self.gap_start.offset(diff);
165                libc::memmove(new_pos as *mut libc::c_void,
166                              self.gap_start as *mut libc::c_void,
167                              diff as usize);
168            }
169        }
170    }
171
172    fn tail(&self) -> String {
173        let tail_len = ptr_diff(self.buf_end, self.gap_end) as usize;
174        string_from_segment(self.gap_end, tail_len)
175    }
176}
177
178impl fmt::Display for GapBuffer {
179    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180        write!(f, "{}{}", self.head(), self.tail())
181    }
182}
183
184impl Drop for GapBuffer {
185    fn drop(&mut self) {
186        unsafe { libc::free(self.buf_start as *mut libc::c_void); }
187    }
188}
189
190fn ptr_to_isize(p: *const u8) -> isize {
191    unsafe { mem::transmute::<*const u8, isize>(p) }
192}
193
194fn ptr_diff(p: *const u8, q: *const u8) -> isize {
195    ptr_to_isize(p) - ptr_to_isize(q)
196}
197
198fn string_from_segment(start: *mut u8, len: usize) -> String {
199    let mut s = String::with_capacity(len);
200    let tmp = unsafe { String::from_raw_parts(start, len, len) };
201    s.push_str(&tmp);
202    mem::forget(tmp);
203    s
204}
205
206
207#[cfg(test)]
208mod tests {
209    use super::GapBuffer;
210
211
212    #[test]
213    fn insert_str_1() {
214        let gap_buf = buf_from_str("12345678");
215        let text = gap_buf.to_string();
216        assert!(text == "12345678");
217    }
218
219    #[test]
220    fn insert_str_2() {
221        let mut gap_buf = buf_from_str("12345678");
222        gap_buf.insert_str(7, "9");
223
224        let text = gap_buf.to_string();
225        assert!(text == "123456798");
226    }
227
228    #[test]
229    fn insert_str_3() {
230        let mut gap_buf = buf_from_str("12345678");
231        gap_buf.insert_str(0, "0");
232
233        let text = gap_buf.to_string();
234        assert!(text == "012345678");
235    }
236
237    #[test]
238    fn insert_str_4() {
239        let mut gap_buf = buf_from_str("0123456789.0123456789.0123456789");
240        gap_buf.insert_str(0, "9876543210.");
241
242        let text = gap_buf.to_string();
243        assert!(text == "9876543210.0123456789.0123456789.0123456789");
244    }
245
246    #[test]
247    fn insert_str_5() {
248        let mut gap_buf = buf_from_str("0123456789.0123456789.0123456789");
249        gap_buf.insert_str(11, "9876543210.");
250
251        let text = gap_buf.to_string();
252        assert!(text == "0123456789.9876543210.0123456789.0123456789");
253    }
254
255    #[test]
256    fn remove_1() {
257        let mut gap_buf = buf_from_str("12345678");
258        gap_buf.remove(0..8);
259
260        let text = gap_buf.to_string();
261        assert!(text == "");
262    }
263
264    #[test]
265    fn remove_2() {
266        let mut gap_buf = buf_from_str("12345678");
267        gap_buf.remove(0..1);
268
269        let text = gap_buf.to_string();
270        assert!(text == "2345678");
271    }
272
273    #[test]
274    fn remove_3() {
275        let mut gap_buf = buf_from_str("12345678");
276        gap_buf.remove(7..8);
277
278        let text = gap_buf.to_string();
279        assert!(text == "1234567");
280    }
281
282    #[test]
283    fn remove_4() {
284        let mut gap_buf = buf_from_str("12345678");
285        gap_buf.remove(3..6);
286
287        let text = gap_buf.to_string();
288        assert!(text == "12378");
289    }
290
291    #[test]
292    #[should_panic]
293    fn remove_5() {
294        let mut gap_buf = buf_from_str("12345678");
295        gap_buf.remove(0..9);
296    }
297
298    fn buf_from_str(s: &str) -> GapBuffer {
299        let mut buf = GapBuffer::with_capacity(s.len());
300        buf.insert_str(0, s);
301        buf
302    }
303}