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 }