On Sat, 18 Oct 2014 00:32:09 +0000
Lucas Burson via Digitalmars-d-learn
<digitalmars-d-learn@puremagic.com> wrote:

> On Friday, 17 October 2014 at 17:40:09 UTC, ketmar via 
> Digitalmars-d-learn wrote:
> 
> > i developed a habit of making such buffers one byte bigger than
> > necessary and just setting the last byte to 0 before 
> > converting. this
> > way it's guaranteed to be 0-terminated.
> 
> Perfect, great idea. Below is my utility method to pull strings 
> out of a buffer.
> 
> 
> /**
>   * Get a string from buffer where the string spans [offset_start, 
> offset_end).
>   * Params:
>   *    buffer = Buffer with an ASCII string to obtain.
>   *    offset_start = Beginning byte offset within the buffer 
> where the string starts.
>   *    offset_end = Ending byte offset which is not included in 
> the string.
>   */
> string bufferGetString(ubyte[] buffer, ulong offset_start, ulong 
> offset_end)
> in
> {
>     assert(buffer != null);
>     assert(offset_start < offset_end);
>     assert(offset_end <= buffer.length);
> }
> body
> {
>     ulong bufflen = offset_end - offset_start;
> 
>     // add one to the lenth for null-termination
>     ubyte[] temp = new ubyte[bufflen+1];
>     temp[0..bufflen] = buffer[offset_start..offset_end];
>     temp[bufflen] = '\0';
> 
>     return strip(to!string(cast(const char*) temp.ptr));
> }
> 
> unittest
> {
>     ubyte[] no_null = [' ', 'A', 'B', 'C', ' '];
>     assert("ABC" == bufferGetString(no_null, 0, no_null.length));
>     assert("ABC" == bufferGetString(no_null, 1, no_null.length-1));
>     assert("A" == bufferGetString(no_null, 1, 2));
> }

note that you can make your code slightly simplier (and more correct):

  size_t bufflen = offset_end-offset_start;

  // add one to the lenth for null-termination
  auto temp = new ubyte[bufflen+1]; // compiler knows the type ;-)
  temp[0..$-1] = buffer[offset_start..offset_end];
  // this is not necessary, as 'temp' is initialized with zeroes
  //temp[$-1] = '\0';

   return strip(to!string(cast(const char*) temp.ptr));

also note that this allocates like crazy. ;-) this can be tolerable,
but good to remember anyway.

besides, slices rocks, so you can just pass a slice there. so:

  string bufferGetString (const(ubyte)[] buffer) {
    import std.conv : to;
    import std.string : strip;
    if (buffer.length == 0) return null; // or ""
    if (buffer[$-1] == 0) return to!string(cast(char*)buffer.ptr).strip;
    auto temp = new ubyte[](buffer.length+1);
    temp[0..$-1] = buffer[];
    return to!string(cast(char*)temp.ptr).strip;
  }

  unittest {
    ubyte[] no_null = [' ', 'A', 'B', 'C', ' '];
    immutable ubyte[] no_nullI = [' ', 'A', 'B', 'C', ' '];
    assert("ABC" == bufferGetString(no_null[0..$]));
    assert("ABC" == bufferGetString(no_null[1..$-1]));
    // look, we can use const/immutable buffers too!
    assert("A" == bufferGetString(no_nullI[1..2]));
  }

slices are cheap, and you'll get range checking at the call site.

Attachment: signature.asc
Description: PGP signature

Reply via email to