1 module as.addons.str; 2 import core.memory; 3 import std.stdio; 4 import std.conv : emplace; 5 import as; 6 import as.utils; 7 8 9 /* 10 String Factory 11 The string factory can use the GC because the strings are allocated via refcount 12 */ 13 private extern(C) { 14 15 // String references 16 struct StrRef { 17 int refs; 18 string data; 19 } 20 21 // Small type that allows moving strings on to the GC heap 22 struct StrPtr { string data; } 23 24 /** 25 The cache of constant values 26 */ 27 StrRef[string] constantCache; 28 29 // FACTORY 30 void* getStringConstant(const(char)* data, uint length) { 31 string text = (cast(string)data[0..length]).idup; 32 33 // Handle adding references to strings 34 if (text !in constantCache) constantCache[text] = StrRef(1, text); 35 else constantCache[text].refs++; 36 37 return cast(void*)&constantCache[text].data; 38 } 39 40 int releaseStringConstant(const(void)* str) { 41 if (str is null) return ReturnCodes.Error; 42 43 string text = *cast(string*)str; 44 45 // Handle releasing strings 46 if (text !in constantCache) return ReturnCodes.Error; 47 else if (--constantCache[text].refs <= 0) constantCache.remove(text); 48 49 return ReturnCodes.Success; 50 } 51 52 int getRawStringData(const(void)* istr, char* data, uint* length) { 53 if (istr is null) return ReturnCodes.Error; 54 55 string str = *cast(string*)istr; 56 57 // If the length is not null set the length 58 if (length !is null) 59 *length = cast(uint)str.length; 60 61 // If the data pointer is not null fill the data buffer 62 if (data !is null) data[0..str.length] = str[0..str.length]; 63 64 // We're done 65 return ReturnCodes.Success; 66 } 67 } 68 69 /** 70 An AngelScript compatible string 71 */ 72 struct ASString { 73 private: 74 size_t length_; 75 const(char)* str_; 76 77 public: 78 79 /** 80 Creates an empty string with specified length 81 */ 82 this(size_t length) { 83 this.length_ = length; 84 this.str_ = cast(const(char)*)cMalloc(length); 85 } 86 87 /** 88 Creates an ASString based on a D string 89 90 GC memory will be copied out 91 */ 92 this(string str) { 93 94 this.str_ = cast(const(char)*)cMalloc(str.length); 95 this.length_ = str.length; 96 97 // Copy memory from GC string to non-GC memory 98 cMemCopy(this.str_, str.ptr, str.length); 99 } 100 101 /** 102 Creates an ASString based on a D string 103 104 GC memory will be copied out 105 */ 106 this(ASString str) { 107 this.length_ = str.length_; 108 this.str_ = cast(const(char)*)cMalloc(str.length_); 109 110 // Copy memory from GC string to non-GC memory 111 cMemCopy(this.str_, str.str_, str.length_); 112 } 113 114 /** 115 Disposes this ASString 116 */ 117 void dispose() { 118 cFree(str_); 119 } 120 121 /** 122 Overrides string assignment 123 */ 124 void opAssign(ref string str) { 125 126 // Free old memory if need be 127 if (length_ > 0) cFree(str_); 128 129 this.str_ = cast(const(char)*)cMalloc(str.length); 130 this.length_ = str.length; 131 132 // Copy memory from GC string to non-GC memory 133 cMemCopy(this.str_, str.ptr, str.length); 134 } 135 136 /** 137 Overrides string assignment 138 */ 139 void opAssign(ASString str) { 140 141 // Free old memory if need be 142 if (length_ > 0) { 143 cFree(str_); 144 } 145 146 this.str_ = cast(const(char)*)cMalloc(str.length_); 147 this.length_ = str.length_; 148 149 // Copy memory from GC string to non-GC memory 150 cMemCopy(this.str_, str.str_, str.length_); 151 } 152 153 /** 154 Returns a new string based on this and an other string 155 */ 156 ASString opBinary(string op = "~")(ref ASString rhs) { 157 158 // Allocates the ASString on the stack 159 ASString newStr = ASString(this.length_+rhs.length_); 160 161 // Copy the data from both strings in order 162 cMemCopy(newStr.str_, str_, length_); 163 cMemCopy(newStr.str_+length_, rhs.str_, rhs.length_); 164 return newStr; 165 } 166 167 /** 168 Allows appending strings 169 */ 170 ASString opOpAssign(string op="~=")(ref ASString rhs) { 171 auto nstr = (this~rhs); 172 173 // Data is copied 174 this = nstr; 175 176 // Destruct the temporary string 177 destruct(nstr); 178 return this; 179 } 180 181 void resize(size_t length) { 182 183 // Cache old data so we can copy later 184 auto oldData = this.str_; 185 186 // Realloate 187 this.str_ = cast(const(char)*)cMalloc(length); 188 189 // Copy the string data from the old string based on whether the old or new size is the smallest 190 cMemCopy(this.str_, oldData, length < this.length_ ? length : length_); 191 192 // Free the memory for the old data 193 cFree(oldData); 194 } 195 196 ASString substr(uint start, uint count) { 197 ASString ret; 198 199 if (start < length_ && count != 0) { 200 201 // -1 = To end of string 202 if (count < 0 || start+count > length_) { 203 count = cast(uint)length_-start; 204 } 205 206 ret.length_ = count; 207 ret.str_ = cast(const(char)*)cMalloc(count); 208 cMemCopy(ret.str_, &str_[start], count); 209 } 210 return ret; 211 } 212 213 /** 214 Gets the length of the string 215 */ 216 size_t length() { 217 return length_; 218 } 219 220 /** 221 Gets whether the string is empty 222 */ 223 bool isEmpty() { 224 return length_ == 0; 225 } 226 227 /** 228 Reinterprets this as a D string 229 230 Do note that ASStrings are *not* garbage collected memory. 231 */ 232 string toString() const { 233 return reinterpret_cast!string(this); 234 } 235 } 236 237 private { 238 void asStringConstruct(ASString* memory) { 239 emplace!ASString(memory); 240 memory.length_ = 0; 241 } 242 243 void asStringCopyConstruct(ref ASString other, ASString* memory) { 244 emplace!ASString(memory); 245 *memory = other; 246 } 247 248 void asStringDestruct(ASString* memory) { 249 destruct(memory); 250 } 251 252 ASString* asStringAssign(ASString* in_, ASString* this_) { 253 *this_ = *in_; 254 return this_; 255 } 256 257 ASString* asStringAddAssign(ASString* in_, ASString* this_) { 258 *this_ ~= *in_; 259 return this_; 260 } 261 262 ASString* asStringAdd(ref ASString this_, ref ASString rhs) { 263 // Allocates the ASString on the heap 264 ASString* newStr = cCreate!ASString(this_.length_+rhs.length_); 265 266 // Copy the data from both strings in order 267 cMemCopy(newStr.str_, this_.str_, this_.length_); 268 cMemCopy(newStr.str_+this_.length_, rhs.str_, rhs.length_); 269 return newStr; 270 } 271 272 ASString* asSubstr(uint start, uint count, ref ASString this_) { 273 // Allocates the ASString on the heap 274 auto substr = this_.substr(start, count); 275 scope(exit) substr.dispose(); 276 277 return cCreate!ASString(substr); 278 } 279 } 280 281 /** 282 Allocates a string on the GC 283 */ 284 string* StringPtr(string text) { 285 StrPtr* strref = new StrPtr(text); 286 return &strref.data; 287 } 288 289 void registerDStrings(ScriptEngine engine) { 290 engine.registerObjectType("string", ASString.sizeof, TypeFlags.Value | TypeFlags.CDAK); 291 engine.registerStringFactory("string", &getStringConstant, &releaseStringConstant, &getRawStringData); 292 293 // Register constructor and destructor 294 engine.registerObjectBehaviour("string", Behaviours.Construct, "void f()", &asStringConstruct, CallConv.DDeclObjLast); 295 engine.registerObjectBehaviour("string", Behaviours.Construct, "void f(const string &in)", &asStringCopyConstruct, CallConv.DDeclObjLast); 296 engine.registerObjectBehaviour("string", Behaviours.Destruct, "void f()", &asStringDestruct, CallConv.DDeclObjLast); 297 298 engine.registerObjectMethod("string", "string &opAssign(const string &in)", &asStringAssign, CallConv.DDeclObjLast); 299 engine.registerObjectMethod("string", "string &opAddAssign(const string &in) const", &asStringAddAssign, CallConv.DDeclObjLast); 300 engine.registerObjectMethod("string", "string &opAdd(const string &in) const", &asStringAdd, CallConv.DDeclObjFirst); 301 302 engine.registerObjectMethod("string", "uint length() const", &ASString.length, CallConv.DDeclObjFirst); 303 engine.registerObjectMethod("string", "bool isEmpty() const", &ASString.isEmpty, CallConv.DDeclObjFirst); 304 engine.registerObjectMethod("string", "uint resize(uint size) const", &ASString.resize, CallConv.DDeclObjFirst); 305 engine.registerObjectMethod("string", "string &substr(uint start = 0, uint count = -1) const", &asSubstr, CallConv.DDeclObjLast); 306 307 // Conversion functionality 308 import std.conv : text; 309 engine.registerGlobalFunction("string &intToString(int64 value)", (long value) { return StringPtr(value.text); }); 310 engine.registerGlobalFunction("string &uintToString(uint64 value)", (ulong value) { return StringPtr(value.text); }); 311 engine.registerGlobalFunction("string &floatToString(double value)", (double value) { return StringPtr(value.text); }); 312 }