1 /**
2  * D-LevelDB Slice
3  *
4  * Pointer Slice
5  *
6  * Copyright: Copyright © 2013 Byron Heads
7  * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
8  * Authors: Byron Heads
9 */
10 /*          Copyright  © 2013 Byron Heads
11  * Distributed under the Boost Software License, Version 1.0.
12  *    (See accompanying file LICENSE_1_0.txt or copy at
13  *          http://www.boost.org/LICENSE_1_0.txt)
14  */
15 module leveldb.slice;
16 
17 private import leveldb.exceptions;
18 private import std.traits : isArray, isStaticArray, isDynamicArray,
19                             isPointer, isBasicType, ForeachType, isSomeString;
20 private import deimos.leveldb.leveldb : leveldb_free;
21 
22 /**
23  * Holds a pointer returned from leveldb, or passed to leveldb.
24  *
25  * Leveldb memory is freed on destruction.
26  */
27 struct Slice
28 {
29 private:
30     bool free = false;
31     void* _ptr;
32     size_t len;
33 
34 package:
35     /// Used by DB class to hold leveldb raw pointers
36     this(P = void*)(void* p, size_t l, bool free)
37     {
38         this.free = free;
39         _ptr = p;
40         len = l;
41     }
42 
43 public:
44     /// Takes reference
45     this(P)(ref P p)
46     {
47         this(p._lib_obj_ptr__, p._lib_obj_size__);
48     }
49 
50     this(P)(in P p)
51         if(!__traits(isRef, p))
52     {
53         this(p._lib_obj_ptr__, p._lib_obj_size__);
54     }
55 
56     /// Takes reference
57     this(P)(P p, size_t l)
58         if(isPointer!P)
59     {
60         _ptr = cast(void*)p;
61         len = l;
62     }
63 
64     /// Calles free on leveldb raw memory
65     ~this()
66     {
67         if(free)
68             leveldb_free(_ptr);
69     }
70 
71     /// Get slice pointer
72     @property
73     inout(T) ptr(T)() inout
74         if(isPointer!T)
75     {
76         return cast(inout(T))_ptr;
77     }
78 
79     alias ptr!(const(char*)) _lib_obj_ptr__;
80 
81     /// Get slice as a data type
82     @property
83     inout(T) as(T)() inout
84         if(!isPointer!T && __traits(compiles, *(cast(inout(T*))_ptr)))
85     {
86         static if(isArray!T)
87             return  cast(inout(T))(cast(char[])(_ptr)[0..length]);
88         else static if(is(T == class))
89         {
90             if(typeid(T).sizeof > length)
91                 throw new LeveldbException("Casting size is larger then slice data");
92             return *(cast(inout(T*))_ptr);
93         }
94         else
95         {
96             if(T.sizeof > length)
97                 throw new LeveldbException("Casting size is larger then slice data");
98             return *(cast(inout(T*))_ptr);
99         }
100     }
101 
102     alias as to;
103 
104     /// length or size of slice
105     @property
106     size_t length() inout
107     {
108         return len;
109     }
110     alias length _lib_obj_size__;
111 
112     /// Test is slice is valid
113     @property
114     bool ok() inout
115     {
116         return _ptr !is null;
117     }
118 
119     /// Slice casting
120     inout(T) opCast(T)() inout
121     {
122         static if(isPointer!T)
123             return ptr!T;
124         else
125             return as!T;
126     }
127 
128     /// Create a safe refrence for slicing, good for primitive type constants
129     static Slice Ref(T)(T t)
130     {
131         align(1) static struct Ref{ T t; }
132         return Slice(new Ref(t), T.sizeof);
133     }
134 }
135 
136 package:
137 
138 /// Find the byte size of a valid Slice type
139 size_t _lib_obj_size__(P)(in P p)
140     if(isSomeString!P || ((isStaticArray!P || isDynamicArray!P) && !isBanned!(ForeachType!P)))
141 {
142     return p.length ? p[0].sizeof * p.length : 0;
143 }
144 
145 /// Find the byte size of a valid Slice type
146 size_t _lib_obj_size__(P)(in P p)
147     if(isBasicType!P || isPODStruct!P) 
148 {
149     return P.sizeof;
150 }
151 
152 /// Find the byte size of a valid Slice type
153 size_t _lib_obj_size__(P)(in P p)
154     if(isPointer!P) 
155 {
156     return _lib_obj_size__(*p);
157 }
158     
159 /// Find the pointer of a valid Slice type
160 const(char)* _lib_obj_ptr__(P)(ref P p)
161 {
162     static if((isArray!P && !isBanned!(ForeachType!P)))
163         return cast(const(char*))p.ptr;
164     else static if(isBasicType!P || isPODStruct!P)
165         return cast(const(char*))(&p);
166     else static if(isPointer!P) 
167         return _lib_obj_ptr__(*p);
168     else assert(false, "Not a valid type for leveldb slice: ref " ~ typeof(p).stringof);
169 }
170 
171 template isBanned(T)
172 {
173     static if(is(T == class) || isDynamicArray!T || isPointer!T)
174         enum isBanned = true;
175     else
176         enum isBanned = false;
177 }
178 
179 template isPODStruct(T)
180 {
181     static if(is(T == struct))
182         enum isPODStruct = __traits(isPOD, T);
183     else
184         enum isPODStruct = false;
185 }
186 
187 unittest
188 {
189     assert(_lib_obj_size__("1234567890") == 10);
190     assert(_lib_obj_size__("1234") == 4);
191     int i = 123567;
192     assert(_lib_obj_size__(i) == int.sizeof);
193     long l = 123567;
194     assert(_lib_obj_size__(l) == long.sizeof);
195     double d = 123567;
196     assert(_lib_obj_size__(d) == double.sizeof);
197 }
198 
199 unittest
200 {
201     auto s = "Hello";
202     auto s1 = Slice(s);
203     assert(s1);
204     assert(s1.ok);
205     assert(s1.length == 5);
206     assert(s1.ptr!(const(char*)) == s.ptr);
207     assert(s1.length == s.length);
208     assert(s1.as!string == s);
209 }
210 
211 unittest
212 {
213     auto s = "Hello World";
214     auto s1 = Slice(s[0..5]);
215     assert(s1.ok);
216     assert(s1.length == 5);
217     assert(s1.ptr!(const(char*)) == s.ptr);
218     assert(s1.length == s[0..5].length);
219     assert(s1.as!string == s[0..5]);
220 }
221 
222 unittest
223 {
224     int s = 454;
225     auto s1 = Slice(s);
226     assert(s1.ok);
227     assert(s1.length == int.sizeof);
228     assert(s1.length == s.sizeof);
229     assert(s1.ptr!(int*) == &s);
230     assert(s1.as!int == s);
231 }
232 
233 unittest
234 {
235     struct Point(T)
236     {
237         T x, y;
238     }
239 
240     auto p1 = Point!int(1, 2);
241     auto s = Slice(p1);
242     assert(s.ok);
243     assert(s.length == int.sizeof * 2);
244     assert(s.as!(Point!int).x == 1);
245     assert(s.as!(Point!int).y == 2);
246     try
247     {
248         s.as!(Point!long);
249         assert(false, "Should have thrown");
250     }catch(LeveldbException e)
251     {}
252     catch(Exception e)
253     {
254         assert(false, "Should have thrown a LeveldbException");
255     }
256 
257     s = Slice(new Point!real(10, 12));
258     assert(s.length == real.sizeof * 2);
259 }
260 
261 
262 
263 unittest
264 {
265     align(1) struct Ref(T) { T t; }
266     auto s1 = Slice(new Ref!int(451), 4);
267     assert(s1.ok);
268     assert(s1.length == int.sizeof);
269     assert(s1.as!int == 451);
270 
271     /// Make a safe constant slice
272     s1 = Slice.Ref(999);
273     assert(s1.ok);
274     assert(s1.length == int.sizeof);
275     assert(s1.as!int == 999);
276 }