Avoid deallocate empty arrays?

Steven Schveighoffer schveiguy at gmail.com
Fri Dec 18 12:37:58 UTC 2020


On 12/17/20 1:10 PM, IGotD- wrote:
> On Thursday, 17 December 2020 at 17:46:59 UTC, Steven Schveighoffer wrote:
>>
>> This isn’t correct. Can you post the code that led you to believe this?
>>
>> -Steve
> 
> Sure.
> 
> 
> import std.algorithm;
> import std.typecons;
> import std.stdio;
> 
> 
> struct Buffer
> {
>      this(size_t size)
>      {
>          m_buffer.reserve = size;
>      }
> 
> 
>      void add(const void[] arr)
>      {
>          m_buffer ~= cast(ubyte[])arr;
>      }
> 
> 
>      string getSome()
>      {
>          if(m_buffer.length > 0)
>          {
>              return cast(string)m_buffer[0..$];
>          }
>          else
>          {
>              return "";
>          }
>      }
> 
>      void remove(size_t size)
>      {
>          m_buffer = m_buffer.remove(tuple(0, size));

Here is where your issue is. It looks like you are removing the first 
size elements of the array. Which moves all the rest to the front. 
However, the array runtime still thinks you have the original number of 
elements in the buffer.

You need to add:

m_buffer.assumeSafeAppend;

This tells the runtime "I'm done with all the elements that are beyond 
this length."

And then it will work as you expect, no reallocation.

>      }
> 
>      ubyte[] m_buffer;
> }
> 
> void main()
> {
>      Buffer b = Buffer(16);
> 
>      b.add("aa");
> 
>      writeln("b.m_buffer.length ", b.m_buffer.length, ", 
> b.m_buffer.capacity ", b.m_buffer.capacity);
> 
>      string s = b.getSome();
> 
>      assert(s == "aa");
> 
>      b.remove(s.length);
> 
>      writeln("b.m_buffer.length ", b.m_buffer.length, ", 
> b.m_buffer.capacity ", b.m_buffer.capacity);
> }
> 
> This will print
> 
> b.m_buffer.length 2, b.m_buffer.capacity 31
> b.m_buffer.length 0, b.m_buffer.capacity 0
> 
> capacity 0, suggests that the array has been deallocated.

This means it has 0 capacity for appending according to the runtime, NOT 
that the array was deallocated. This is true of non-GC allocated slices 
and slices which don't END at the array end.

For example:

auto arr = [1, 2, 3];

auto arr2 = arr[0 .. 2]; // slice off the last element

assert(arr2.capacity == 0);
assert(arr.capacity != 0);

Does this mean the array is deallocated? No, it means that if you 
append, there is no capacity to add to. A capacity of 0 means "will 
reallocate if you append".

Why does this happen? Because we don't want to stomp on the existing 
data that could still be referenced via another slice (in this case arr) 
that still points to the original data.

You can read a bit about the array runtime here: 
https://dlang.org/articles/d-array-article.html

-Steve


More information about the Digitalmars-d-learn mailing list