mirror of
https://github.com/dlang/phobos.git
synced 2025-04-27 13:40:20 +03:00
add std.internal.scopebuffer
This commit is contained in:
parent
e6e323175d
commit
cb6b8a010f
4 changed files with 388 additions and 5 deletions
|
@ -208,7 +208,7 @@ EXTRA_MODULES += $(EXTRA_DOCUMENTABLES) $(addprefix \
|
|||
std/internal/digest/, sha_SSSE3 ) $(addprefix \
|
||||
std/internal/math/, biguintcore biguintnoasm biguintx86 \
|
||||
gammafunction errorfunction) $(addprefix std/internal/, \
|
||||
processinit uni uni_tab unicode_tables \
|
||||
processinit uni uni_tab unicode_tables scopebuffer \
|
||||
unicode_comp unicode_decomp unicode_grapheme unicode_norm)
|
||||
|
||||
# Aggregate all D modules relevant to this build
|
||||
|
|
383
std/internal/scopebuffer.d
Normal file
383
std/internal/scopebuffer.d
Normal file
|
@ -0,0 +1,383 @@
|
|||
|
||||
/*
|
||||
* Copyright: 2014 by Digital Mars
|
||||
* License: $(LINK2 http://boost.org/LICENSE_1_0.txt, Boost License 1.0).
|
||||
* Authors: Walter Bright
|
||||
* Source: $(PHOBOSSRC std/internal/_scopebuffer.d)
|
||||
*/
|
||||
|
||||
module std.internal.scopebuffer;
|
||||
|
||||
|
||||
//debug=ScopeBuffer;
|
||||
|
||||
private import core.exception;
|
||||
private import core.stdc.stdlib : realloc;
|
||||
private import std.traits;
|
||||
|
||||
/**************************************
|
||||
* ScopeBuffer encapsulates using a local array as a temporary buffer.
|
||||
* It is initialized with the local array that should be large enough for
|
||||
* most uses. If the need exceeds the size, ScopeBuffer will resize it
|
||||
* using malloc() and friends.
|
||||
*
|
||||
* ScopeBuffer cannot contain more than (uint.max-16)/2 elements.
|
||||
*
|
||||
* ScopeBuffer is an OutputRange.
|
||||
*
|
||||
* Since ScopeBuffer potentially stores elements of type T in malloc'd memory,
|
||||
* those elements are not scanned when the GC collects. This can cause
|
||||
* memory corruption. Do not use ScopeBuffer when elements of type T point
|
||||
* to the GC heap.
|
||||
*
|
||||
* Example:
|
||||
---
|
||||
import core.stdc.stdio;
|
||||
import std.internal.scopebuffer;
|
||||
void main()
|
||||
{
|
||||
char[2] buf = void;
|
||||
auto textbuf = ScopeBuffer!char(buf);
|
||||
scope(exit) textbuf.free(); // necessary for cleanup
|
||||
|
||||
// Put characters and strings into textbuf, verify they got there
|
||||
textbuf.put('a');
|
||||
textbuf.put('x');
|
||||
textbuf.put("abc");
|
||||
assert(textbuf.length == 5);
|
||||
assert(textbuf[1..3] == "xa");
|
||||
assert(textbuf[3] == 'b');
|
||||
|
||||
// Can shrink it
|
||||
textbuf.length = 3;
|
||||
assert(textbuf[0..textbuf.length] == "axa");
|
||||
assert(textbuf[textbuf.length - 1] == 'a');
|
||||
assert(textbuf[1..3] == "xa");
|
||||
|
||||
textbuf.put('z');
|
||||
assert(textbuf[] == "axaz");
|
||||
|
||||
// Can shrink it to 0 size, and reuse same memory
|
||||
textbuf.length = 0;
|
||||
}
|
||||
---
|
||||
* It is invalid to access ScopeBuffer's contents when ScopeBuffer goes out of scope.
|
||||
* Hence, copying the contents are necessary to keep them around:
|
||||
---
|
||||
import std.internal.scopebuffer;
|
||||
string cat(string s1, string s2)
|
||||
{
|
||||
char[10] tmpbuf = void;
|
||||
auto textbuf = ScopeBuffer!char(tmpbuf);
|
||||
scope(exit) textbuf.free();
|
||||
textbuf.put(s1);
|
||||
textbuf.put(s2);
|
||||
textbuf.put("even more");
|
||||
return textbuf[].idup;
|
||||
}
|
||||
---
|
||||
* ScopeBuffer is intended for high performance usages in $(D @system) and $(D @trusted) code.
|
||||
* It is designed to fit into two 64 bit registers, again for high performance use.
|
||||
* If used incorrectly, memory leaks and corruption can result. Be sure to use
|
||||
* $(D scope(exit) textbuf.free();) for proper cleanup, and do not refer to a ScopeBuffer
|
||||
* instance's contents after $(D ScopeBuffer.free()) has been called.
|
||||
*
|
||||
* The realloc parameter defaults to C's realloc(). Another can be supplied to override it.
|
||||
*
|
||||
* ScopeBuffer instances may be copied, as in:
|
||||
---
|
||||
textbuf = doSomething(textbuf, args);
|
||||
---
|
||||
* which can be very efficent, but these must be regarded as a move rather than a copy.
|
||||
* Additionally, the code between passing and returning the instance must not throw
|
||||
* exceptions, otherwise when ScopeBuffer.free() is called, memory may get corrupted.
|
||||
*/
|
||||
|
||||
@system
|
||||
struct ScopeBuffer(T, alias realloc = core.stdc.stdlib.realloc)
|
||||
if (isAssignable!T &&
|
||||
!hasElaborateDestructor!T &&
|
||||
!hasElaborateCopyConstructor!T &&
|
||||
!hasElaborateAssign!T)
|
||||
{
|
||||
import core.stdc.string : memcpy;
|
||||
|
||||
/**************************
|
||||
* Initialize with buf to use as scratch buffer space.
|
||||
* Params:
|
||||
* buf = Scratch buffer space, must have length that is even
|
||||
* Example:
|
||||
* ---
|
||||
* ubyte[10] tmpbuf = void;
|
||||
* auto sbuf = ScopeBuffer!ubyte(tmpbuf);
|
||||
* ---
|
||||
* If buf was created by the same realloc passed as a parameter
|
||||
* to ScopeBuffer, then the contents of ScopeBuffer can be extracted without needing
|
||||
* to copy them, and ScopeBuffer.free() will not need to be called.
|
||||
*/
|
||||
this(T[] buf)
|
||||
in
|
||||
{
|
||||
assert(!(buf.length & wasResized)); // assure even length of scratch buffer space
|
||||
assert(buf.length <= uint.max); // because we cast to uint later
|
||||
}
|
||||
body
|
||||
{
|
||||
this.buf = buf.ptr;
|
||||
this.bufLen = cast(uint)buf.length;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
ubyte[10] tmpbuf = void;
|
||||
auto sbuf = ScopeBuffer!ubyte(tmpbuf);
|
||||
}
|
||||
|
||||
/**************************
|
||||
* Releases any memory used.
|
||||
* This will invalidate any references returned by the [] operator.
|
||||
* A destructor is not used, because that would make it not POD
|
||||
* (Plain Old Data) and it could not be placed in registers.
|
||||
*/
|
||||
void free()
|
||||
{
|
||||
debug(ScopeBuffer) buf[0 .. bufLen] = 0;
|
||||
if (bufLen & wasResized)
|
||||
realloc(buf, 0);
|
||||
buf = null;
|
||||
bufLen = 0;
|
||||
used = 0;
|
||||
}
|
||||
|
||||
/****************************
|
||||
* Copying of ScopeBuffer is not allowed.
|
||||
*/
|
||||
//@disable this(this);
|
||||
|
||||
/************************
|
||||
* Append element c to the buffer.
|
||||
* This member function makes ScopeBuffer an OutputRange.
|
||||
*/
|
||||
void put(T c)
|
||||
{
|
||||
/* j will get enregistered, while used will not because resize() may change used
|
||||
*/
|
||||
const j = used;
|
||||
if (j == bufLen)
|
||||
{
|
||||
assert(j <= (uint.max - 16) / 2);
|
||||
resize(j * 2 + 16);
|
||||
}
|
||||
buf[j] = c;
|
||||
used = j + 1;
|
||||
}
|
||||
|
||||
/************************
|
||||
* Append array s to the buffer.
|
||||
*/
|
||||
void put(const(T)[] s)
|
||||
{
|
||||
const newlen = used + s.length;
|
||||
assert((cast(ulong)used + s.length) <= uint.max);
|
||||
const len = bufLen;
|
||||
if (newlen > len)
|
||||
{
|
||||
assert(len <= uint.max / 2);
|
||||
resize(newlen <= len * 2 ? len * 2 : newlen);
|
||||
}
|
||||
buf[used .. newlen] = cast(T[])s[];
|
||||
used = cast(uint)newlen;
|
||||
}
|
||||
|
||||
/******
|
||||
* Retrieve a slice into the result.
|
||||
* Returns:
|
||||
* A slice into the temporary buffer that is only
|
||||
* valid until the next put() or ScopeBuffer goes out of scope.
|
||||
*/
|
||||
@system T[] opSlice(size_t lower, size_t upper)
|
||||
in
|
||||
{
|
||||
assert(lower <= bufLen);
|
||||
assert(upper <= bufLen);
|
||||
assert(lower <= upper);
|
||||
}
|
||||
body
|
||||
{
|
||||
return buf[lower .. upper];
|
||||
}
|
||||
|
||||
/// ditto
|
||||
@system T[] opSlice()
|
||||
{
|
||||
assert(used <= bufLen);
|
||||
return buf[0 .. used];
|
||||
}
|
||||
|
||||
/*******
|
||||
* Returns:
|
||||
* the element at index i.
|
||||
*/
|
||||
ref T opIndex(size_t i)
|
||||
{
|
||||
assert(i < bufLen);
|
||||
return buf[i];
|
||||
}
|
||||
|
||||
/***
|
||||
* Returns:
|
||||
* the number of elements in the ScopeBuffer
|
||||
*/
|
||||
@property size_t length() const
|
||||
{
|
||||
return used;
|
||||
}
|
||||
|
||||
/***
|
||||
* Used to shrink the length of the buffer,
|
||||
* typically to 0 so the buffer can be reused.
|
||||
* Cannot be used to extend the length of the buffer.
|
||||
*/
|
||||
@property void length(size_t i)
|
||||
in
|
||||
{
|
||||
assert(i <= this.used);
|
||||
}
|
||||
body
|
||||
{
|
||||
this.used = cast(uint)i;
|
||||
}
|
||||
|
||||
alias opDollar = length;
|
||||
|
||||
private:
|
||||
T* buf;
|
||||
// Using uint instead of size_t so the struct fits in 2 registers in 64 bit code
|
||||
uint bufLen;
|
||||
enum wasResized = 1; // this bit is set in bufLen if we control the memory
|
||||
uint used;
|
||||
|
||||
void resize(size_t newsize)
|
||||
in
|
||||
{
|
||||
assert(newsize <= uint.max);
|
||||
}
|
||||
body
|
||||
{
|
||||
//writefln("%s: oldsize %s newsize %s", id, buf.length, newsize);
|
||||
newsize |= wasResized;
|
||||
void *newBuf = realloc((bufLen & wasResized) ? buf : null, newsize * T.sizeof);
|
||||
if (!newBuf)
|
||||
core.exception.onOutOfMemoryError();
|
||||
if (!(bufLen & wasResized))
|
||||
{
|
||||
memcpy(newBuf, buf, used * T.sizeof);
|
||||
debug(ScopeBuffer) buf[0 .. bufLen] = 0;
|
||||
}
|
||||
buf = cast(T*)newBuf;
|
||||
bufLen = cast(uint)newsize;
|
||||
|
||||
/* This function is called only rarely,
|
||||
* inlining results in poorer register allocation.
|
||||
*/
|
||||
version (DigitalMars)
|
||||
/* With dmd, a fake loop will prevent inlining.
|
||||
* Using a hack until a language enhancement is implemented.
|
||||
*/
|
||||
while (1) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
import core.stdc.stdio;
|
||||
import std.range;
|
||||
|
||||
char[2] tmpbuf = void;
|
||||
{
|
||||
// Exercise all the lines of code except for assert(0)'s
|
||||
auto textbuf = ScopeBuffer!char(tmpbuf);
|
||||
scope(exit) textbuf.free();
|
||||
|
||||
static assert(isOutputRange!(ScopeBuffer!char, char));
|
||||
|
||||
textbuf.put('a');
|
||||
textbuf.put('x');
|
||||
textbuf.put("abc"); // tickle put([])'s resize
|
||||
assert(textbuf.length == 5);
|
||||
assert(textbuf[1..3] == "xa");
|
||||
assert(textbuf[3] == 'b');
|
||||
|
||||
textbuf.length = textbuf.length - 1;
|
||||
assert(textbuf[0..textbuf.length] == "axab");
|
||||
|
||||
textbuf.length = 3;
|
||||
assert(textbuf[0..textbuf.length] == "axa");
|
||||
assert(textbuf[textbuf.length - 1] == 'a');
|
||||
assert(textbuf[1..3] == "xa");
|
||||
|
||||
textbuf.put(cast(dchar)'z');
|
||||
assert(textbuf[] == "axaz");
|
||||
|
||||
textbuf.length = 0; // reset for reuse
|
||||
assert(textbuf.length == 0);
|
||||
|
||||
foreach (char c; "asdf;lasdlfaklsdjfalksdjfa;lksdjflkajsfdasdfkja;sdlfj")
|
||||
{
|
||||
textbuf.put(c); // tickle put(c)'s resize
|
||||
}
|
||||
assert(textbuf[] == "asdf;lasdlfaklsdjfalksdjfa;lksdjflkajsfdasdfkja;sdlfj");
|
||||
} // run destructor on textbuf here
|
||||
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
string cat(string s1, string s2)
|
||||
{
|
||||
char[10] tmpbuf = void;
|
||||
auto textbuf = ScopeBuffer!char(tmpbuf);
|
||||
scope(exit) textbuf.free();
|
||||
textbuf.put(s1);
|
||||
textbuf.put(s2);
|
||||
textbuf.put("even more");
|
||||
return textbuf[].idup;
|
||||
}
|
||||
|
||||
auto s = cat("hello", "betty");
|
||||
assert(s == "hellobettyeven more");
|
||||
}
|
||||
|
||||
/*********************************
|
||||
* This is a slightly simpler way to create a ScopeBuffer instance
|
||||
* that uses type deduction.
|
||||
* Params:
|
||||
* tmpbuf = the initial buffer to use
|
||||
* Returns:
|
||||
* an instance of ScopeBuffer
|
||||
* Example:
|
||||
---
|
||||
ubyte[10] tmpbuf = void;
|
||||
auto sb = scopeBuffer(tmpbuf);
|
||||
scope(exit) sp.free();
|
||||
---
|
||||
*/
|
||||
|
||||
auto scopeBuffer(T)(T[] tmpbuf)
|
||||
{
|
||||
return ScopeBuffer!T(tmpbuf);
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
ubyte[10] tmpbuf = void;
|
||||
auto sb = scopeBuffer(tmpbuf);
|
||||
scope(exit) sb.free();
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
ScopeBuffer!(int*) b;
|
||||
int*[] s;
|
||||
b.put(s);
|
||||
}
|
|
@ -192,8 +192,8 @@ SRC_STD_C_OSX= std\c\osx\socket.d
|
|||
SRC_STD_C_FREEBSD= std\c\freebsd\socket.d
|
||||
|
||||
SRC_STD_INTERNAL= std\internal\processinit.d std\internal\uni.d std\internal\uni_tab.d \
|
||||
std\internal\unicode_tables.d std\internal\unicode_comp.d std\internal\unicode_decomp.d \
|
||||
std\internal\unicode_grapheme.d std\internal\unicode_norm.d
|
||||
std\internal\unicode_tables.d std\internal\unicode_comp.d std\internal\unicode_decomp.d \
|
||||
std\internal\unicode_grapheme.d std\internal\unicode_norm.d std\internal\scopebuffer.d
|
||||
|
||||
SRC_STD_INTERNAL_DIGEST= std\internal\digest\sha_SSSE3.d
|
||||
|
||||
|
|
|
@ -212,7 +212,7 @@ SRC_STD_C_FREEBSD= std\c\freebsd\socket.d
|
|||
|
||||
SRC_STD_INTERNAL= std\internal\processinit.d std\internal\uni.d std\internal\uni_tab.d \
|
||||
std\internal\unicode_tables.d std\internal\unicode_comp.d std\internal\unicode_decomp.d \
|
||||
std\internal\unicode_grapheme.d std\internal\unicode_norm.d
|
||||
std\internal\unicode_grapheme.d std\internal\unicode_norm.d std\internal\scopebuffer.d
|
||||
|
||||
SRC_STD_INTERNAL_DIGEST= std\internal\digest\sha_SSSE3.d
|
||||
|
||||
|
@ -749,7 +749,7 @@ $(DOC)\etc_c_zlib.html : $(STDDOC) etc\c\zlib.d
|
|||
zip : win32.mak win64.mak posix.mak $(STDDOC) $(SRC) \
|
||||
$(SRC_STD) $(SRC_STD_C) $(SRC_STD_WIN) \
|
||||
$(SRC_STD_C_WIN) $(SRC_STD_C_LINUX) $(SRC_STD_C_OSX) $(SRC_STD_C_FREEBSD) \
|
||||
$(SRC_ETC) $(SRC_ETC_C) $(SRC_ZLIB) $(SRC_STD_NET) $(SRC_STD_DIGEST)\
|
||||
$(SRC_ETC) $(SRC_ETC_C) $(SRC_ZLIB) $(SRC_STD_NET) $(SRC_STD_DIGEST) \
|
||||
$(SRC_STD_INTERNAL) $(SRC_STD_INTERNAL_DIGEST) $(SRC_STD_INTERNAL_MATH) \
|
||||
$(SRC_STD_INTERNAL_WINDOWS)
|
||||
del phobos.zip
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue