Binary slices¶
#include <libcork/ds.h>
This section defines an interface for safely referring to the contents of a binary buffer, without needing to know where the buffer came from. In addition to accessing the contents of the underlying buffer, slices support three operations:
- Copying initializes a new slice object to point at the same
underlying buffer as the current slice. Depending on how the
underlying buffer is implemented, this doesn’t necessarily involve
actual copying; for instance, the
cork_managed_buffer
type implements this operation by incrementing the reference count of the managed buffer. - Slicing updates the current slice to point at a subset of its current contents. This doesn’t affect the underlying buffer.
- Freeing releases the resources used by the slice, possibly freeing the underlying buffer.
These operations are represented by the slice interface
(cork_slice_iface
). To write a new slice implementation, you
just need to provide an instance of this interface type.
Note
There’s no generic constructor or initialization function for slices;
instead, you’ll create a slice from some other data structure, using
a function specific to that data structure. (An example is the
cork_buffer_to_slice()
function, which initializes a slice
from a resizable buffer.)
-
struct
cork_slice
¶ A specific window into a portion of an underlying binary buffer.
Instances of this class do not need to be (and almost never are) allocated on the heap; you can define an instance directly on the stack, or in some other kind of managed storage.
Note
It is very important that you ensure that
cork_slice_finish()
is called whenever you are done with a slice — if you don’t, there’s a very good chance that the underlying buffer will never be freed. Yes, yes, it’s unfortunate that C doesn’t havetry
/finally
or RAII, but suck it up and make sure thatcork_slice_finish()
gets called.-
const void *
buf
¶ The beginning of the sliced portion of the underlying buffer.
-
size_t
size
¶ The size of the sliced portion of the underlying buffer.
-
struct cork_slice_iface *
iface
¶ The slice implementation of the underlying buffer. For slice consumers, this field should be considered private. For slice implementors, you should fill in this field with your slice interface.
-
void *
user_data
¶ An opaque pointer used by the slice implementation. For slice consumers, this field should be considered private. For slice implementors, you can use this field to point at the underlying buffer (and/or any additional metadata that you need.)
-
const void *
-
void
cork_slice_clear
(struct cork_slice *slice)¶ Clear a slice object. This fills in a slice instance so that it’s “empty”. You should not try to call any of the slice methods on an empty slice, nor should you try to dereference the slice’s
buf
pointer. An empty slice is equivalent to aNULL
pointer.
-
bool
cork_slice_is_empty
(struct cork_slice *slice)¶ Return whether a slice is empty.
-
int
cork_slice_copy
(struct cork_slice *dest, struct cork_slice *src, size_t offset, size_t length)¶ -
int
cork_slice_copy_offset
(struct cork_slice *dest, struct cork_slice *src, size_t offset)¶ -
int
cork_slice_copy_fast
(struct cork_slice *dest, struct cork_slice *src, size_t offset, size_t length)¶ -
int
cork_slice_copy_offset_fast
(struct cork_slice *dest, struct cork_slice *src, size_t offset)¶ Initialize a new slice that refers to a subset of an existing slice. The offset and length parameters identify the subset. (For the
_copy_offset
variant, the length is calculated automatically to include all of the original slice content starting from offset.)For the
_fast
variants, we don’t verify that the offset and length parameters refer to a valid subset of the slice. This is your responsibility. For the non-_fast
variants, we perform a bounds check for you, and return an error if the requested slice is invalid.Regardless of whether the new slice is valid, you must ensure that you call
cork_slice_finish()
on dest when you are done with it.
-
int
cork_slice_light_copy
(struct cork_slice *dest, const struct cork_slice *src, size_t offset, size_t length)¶ -
int
cork_slice_light_copy_offset
(struct cork_slice *dest, const struct cork_slice *src, size_t offset)¶ -
int
cork_slice_light_copy_fast
(struct cork_slice *dest, const struct cork_slice *src, size_t offset, size_t length)¶ -
int
cork_slice_light_copy_offset_fast
(struct cork_slice *dest, const struct cork_slice *src, size_t offset)¶ Initialize a new slice that refers to a subset of an existing slice. By calling a
_light_copy
function instead of a_copy
function, you are guaranteeing that dest will not outlive src — i.e., it is your responsibility to ensure that you callcork_slice_finish()
on dest before you call it on src. This guarantee lets slice implementations make a more light-weight copy of the slice: for instance, by not having to make a copy of the underlying buffer.The offset and length parameters identify the subset. (For the
_light_copy_offset
variant, the length is calculated automatically to include all of the original slice content starting from offset.)For the
_fast
variants, we don’t verify that the offset and length parameters refer to a valid subset of the slice. This is your responsibility. For the non-_fast
variants, we perform a bounds check for you, and return an error if the requested slice is invalid.Regardless of whether the new slice is valid, you must ensure that you call
cork_slice_finish()
on dest when you are done with it.
-
int
cork_slice_slice
(struct cork_slice *slice, size_t offset, size_t length)¶ -
int
cork_slice_slice_offset
(struct cork_slice *slice, size_t offset)¶ -
int
cork_slice_slice_fast
(struct cork_slice *slice, size_t offset, size_t length)¶ -
int
cork_slice_slice_offset_fast
(struct cork_slice *slice, size_t offset)¶ Update a slice to refer to a subset of its contents. The offset and length parameters identify the subset. (For the
_slice_offset
variant, the length is calculated automatically to include all of the original slice content starting from offset.)For the
_fast
variants, we don’t verify that the offset and length parameters refer to a valid subset of the slice. This is your responsibility. For the non-_fast
variants, we perform a bounds check for you, and return an error if the requested slice is invalid.
-
void
cork_slice_finish
(struct cork_slice *slice)¶ Finalize a slice, freeing the underlying buffer if necessary.
-
int
cork_slice_equal
(const struct cork_slice *slice1, const struct cork_slice *slice2)¶ Compare the contents of two slices for equality. (The contents of the slices are compared, not their pointers; this is the slice equivalent of
memcmp
, not the==
operator.)
Slice interface¶
-
struct
cork_slice_iface
¶ The interface of methods that slice implementations must provide.
-
void
(*free)
(struct cork_slice *self)¶ Called when the slice should be freed. If necessary, you should free the contents of the underlying buffer. (If the buffer contents can be shared, it’s up to you to keep track of when the contents are safe to be freed.)
This function pointer can be
NULL
if you don’t need to free any underlying buffer.
-
int
(*copy)
(struct cork_slice *dest, const struct cork_slice *src, size_t offset, size_t length)¶ -
int
(*light_copy)
(struct cork_slice *dest, const struct cork_slice *src, size_t offset, size_t length)¶ Create a copy of a slice. You can assume that offset and length refer to a valid subset of src’s content.
For the
light_copy
method, the caller guarantees that the new light copy (dest) will not outlive the original slice (src). For some slice implementations, this lets you create a more light-weight copy — for instance, by not having to make an actualy copy of the underlying buffer.
-
int
(*slice)
(struct cork_slice *self, size_t offset, size_t length)¶ Update self to point at a different subset of the underlying buffer. You can assume that offset and length refer to a valid subset of the buffer. (They will be relative to self’s existing slice, and not to the original buffer.)
This function pointer can be
NULL
if you don’t need to do anything special to the underlying buffer; in this case,cork_slice_slice()
andcork_slice_slice_offset()
will update the slice’s buf and size fields for you.
-
void
Built-in slice implementations¶
Several libcork classes can be used to initialize a slice:
- Managed buffers via the
cork_managed_buffer_slice()
function - Resizable buffers via the
cork_buffer_to_slice()
function
You can also initialize a slice to point at an existing buffer:
-
void
cork_slice_init_static
(struct cork_slice *dest, const void *buf, size_t size)¶ Initializes dest to point at the given static buffer. Since the buffer is static, and guaranteed to always exist, the slice’s
copy
method doesn’t copy the underlying data, it just creates a new pointer to the existing buffer.Note
You can also use this function to refer to a non-static buffer, but then you take responsibility for ensuring that the underlying buffer exists for at least as long as the slice, and any copies made of the slice.
As with all slices, you must ensure that you call
cork_slice_finish()
when you’re done with the slice.
-
void
cork_slice_init_copy_once
(struct cork_slice *dest, const void *buf, size_t size)¶ Initializes dest to point at the given buffer. If any copies are made of the slice, then we create a managed copy of the underlying buffer. This means that you only have to ensure that buf exists for as long as the original dest slice is used.
As with all slices, you must ensure that you call
cork_slice_finish()
when you’re done with the slice.