1 module as.addons.str; 2 import as.def; 3 import as.engine; 4 import core.memory; 5 import std.stdio; 6 7 private extern(C) { 8 9 /** 10 A proto string 11 */ 12 union protostring { 13 struct pstr_impl { 14 size_t len; 15 const(char)* data; 16 } 17 18 this(size_t length, const(char)* data) { 19 proto = pstr_impl(length, data); 20 GC.addRoot(&this); 21 GC.setAttr(&this, GC.BlkAttr.NO_MOVE); 22 } 23 24 this(string str) { 25 this.str = str.idup; 26 GC.addRoot(&this); 27 GC.setAttr(&this, GC.BlkAttr.NO_MOVE); 28 } 29 30 void release() { 31 GC.removeRoot(&this); 32 } 33 34 size_t length() { 35 return proto.len; 36 } 37 38 void setText(string text) { 39 GC.removeRoot(cast(void*)str); 40 str = text.idup; 41 GC.addRoot(cast(void*)str); 42 } 43 44 pstr_impl proto; 45 string str; 46 } 47 48 /** 49 The cache of constant values 50 */ 51 int[protostring*] constantCache; 52 53 // FACTORY 54 void* getStringConstant(const(char)* data, asUINT length) { 55 protostring* text = new protostring(cast(string)data[0..length]); 56 57 // Handle adding references to strings 58 if (text !in constantCache) constantCache[text] = 1; 59 else constantCache[text]++; 60 61 return cast(void*)text; 62 } 63 64 int releaseStringConstant(const(void)* str) { 65 if (str is null) return asERetCodes.asERROR; 66 67 auto text = cast(protostring*)str; 68 69 // Handle releasing strings 70 if (text !in constantCache) return asERetCodes.asERROR; 71 else if (--constantCache[text] <= 0) constantCache.remove(text); 72 73 return asERetCodes.asSUCCESS; 74 } 75 76 int getRawStringData(const(void)* istr, char* data, asUINT* length) { 77 if (istr is null) return asERetCodes.asERROR; 78 79 protostring* proto = cast(protostring*)istr; 80 81 // If the length is not null set the length 82 if (length !is null) 83 *length = cast(uint)proto.length; 84 85 // If the data pointer is not null fill the data buffer 86 if (data !is null) 87 data[0..proto.length] = proto.str[0..proto.length]; 88 89 // We're done 90 return asERetCodes.asSUCCESS; 91 } 92 93 void strConstructor(protostring* self) { 94 self = new protostring; 95 96 GC.addRoot(self); 97 GC.setAttr(self, GC.BlkAttr.NO_MOVE); 98 } 99 100 void strDestructor(protostring* self) { 101 GC.removeRoot(self); 102 GC.clrAttr(self, GC.BlkAttr.FINALIZE | GC.BlkAttr.NO_MOVE); 103 } 104 105 protostring* strOpAssign(ref string in_, protostring* self) { 106 self.setText(in_); 107 return self; 108 } 109 110 protostring* strOpAddAssign(ref string in_, protostring* self) { 111 self.setText(self.str~in_); 112 return self; 113 } 114 115 protostring* strOpAdd(protostring* lhs, ref string rhs) { 116 return new protostring(lhs.str~rhs); 117 } 118 119 uint strLength(protostring* self) { 120 return cast(uint)self.length; 121 } 122 123 bool strEmpty(protostring* self) { 124 return self.length == 0; 125 } 126 127 void strResize(uint size, protostring* self) { 128 string newString = self.str.idup; 129 newString.length = size; 130 self.setText(newString); 131 } 132 133 protostring* strSubstr(uint start, uint length, protostring* self) { 134 135 // TODO: Slice via code points? 136 return new protostring(self.str[start..start+length]); 137 } 138 } 139 140 /** 141 Makes a copy of a string on the heap and gets a pointer to it 142 */ 143 string* toAS(string text) { 144 protostring* proto = new protostring(text.idup); 145 return &proto.str; 146 } 147 148 /** 149 Registers D UTF-8 strings 150 */ 151 void registerDStrings(ScriptEngine engine) { 152 engine.registerObjectType("string", string.sizeof, asEObjTypeFlags.asOBJ_VALUE | asEObjTypeFlags.asOBJ_APP_CLASS_CDAK); 153 engine.registerStringFactory("string", &getStringConstant, &releaseStringConstant, &getRawStringData); 154 engine.registerObjectBehaviour("string", asEBehaviours.asBEHAVE_CONSTRUCT, "void f()", &strConstructor, asECallConvTypes.asCALL_CDECL_OBJLAST); 155 engine.registerObjectBehaviour("string", asEBehaviours.asBEHAVE_DESTRUCT, "void f()", &strDestructor, asECallConvTypes.asCALL_CDECL_OBJLAST); 156 157 engine.registerObjectMethod("string", "string &opAssign(const string &in)", &strOpAssign, asECallConvTypes.asCALL_CDECL_OBJLAST); 158 engine.registerObjectMethod("string", "string &opAddAssign(const string &in)", &strOpAddAssign, asECallConvTypes.asCALL_CDECL_OBJLAST); 159 engine.registerObjectMethod("string", "string &opAdd(const string &in)", &strOpAdd, asECallConvTypes.asCALL_CDECL_OBJFIRST); 160 161 engine.registerObjectMethod("string", "uint length() const", &strLength, asECallConvTypes.asCALL_CDECL_OBJLAST); 162 engine.registerObjectMethod("string", "bool isEmpty() const", &strEmpty, asECallConvTypes.asCALL_CDECL_OBJLAST); 163 engine.registerObjectMethod("string", "void resize(uint size) const", &strResize, asECallConvTypes.asCALL_CDECL_OBJLAST); 164 engine.registerObjectMethod("string", "string &substr(uint start, uint length)", &strSubstr, asECallConvTypes.asCALL_CDECL_OBJLAST); 165 }