//builder 字符串生成器 namespace string; class builder{ ctor(len){ this.ptr = ..raw.realloc( len : 1024); ..table.gc(this,"free"); }; @_meta } namespace builder{ ::Msvcrt := ..raw.loadDll("Msvcrt.dll",,"cdecl"); var strpbrk = ::Msvcrt.strpbrk; var strstr = ::Msvcrt.strstr; var strtok = ::Msvcrt.strtok; var snprintf = ::Msvcrt._snprintf; var concat = ..raw.concat; var convert = ..raw.convert; var sizeof = ..raw.sizeof; var getString = ..raw.tostring; is = lambda (v) v@ == _meta; _meta = { _type = "string.builder"; empty = function(){ return ! sizeof(owner[["ptr"]]) }; capacity = function(){ var l,c = sizeof(owner[["ptr"]]); return c:0; }; reserve = function(size){ var l = sizeof(owner[["ptr"]]); if( size < l ) size = l; owner[["ptr"]] = ..raw.realloc(size: 1024,owner[["ptr"]]); return owner; }; size = function(){ return (sizeof(owner[["ptr"]])) }; resize = function(size){ var l,c = sizeof(owner[["ptr"]]); if(l==size) return; if(size>c) owner[["ptr"]] = ..raw.realloc(size+1024,owner[["ptr"]]); convert( {INT size = size},topointer(owner[["ptr"]],-4)); }; assign = function(v){ owner[["ptr"]] = ..raw.realloc( (v[["_struct"]]?sizeof(v):#v ) +1024,owner[["ptr"]],v); }; find = function(pattern,i){ var p = owner[["ptr"]]; return ..raw.find(p,sizeof(p),pattern,i); raw.match( }; match = function(pattern,i){ var p = owner[["ptr"]]; return ..raw.match(p,sizeof(p),pattern,i); }; replace = function(pattern,repl,count){ var tRepl = type(repl); var task = [] var i,j; while( var r = [owner.find(pattern,j)] ){ i,j = ..table.shift(r,2); if(!i) break; if(count!==null){ if(count<=0) break; count--; } var newStr; var repl = repl; var tRepl = tRepl; if(tRepl=="table"){ repl = repl[owner.sliceString(i,j)] tRepl = type(repl); } if(tRepl==="string"){ var sb = owner; newStr = ..string.replace(repl,"\\([\d\\])" ,function(c){ var n = tonumber(c); if(n) return r[n] elseif(n===0) return sb.sliceString(i,j) return c; } ) } elseif(tRepl==="function"){ newStr = repl(..table.unpackArgs(r)) } if(newStr){ ..table.push(task,[i,j-i+1,newStr]); } j++; } for(i=#task;1;-1){ var t = task[i] owner.splice(t[1],t[2],t[3]); } return owner,#task; }; indexAny = function(set){ var p = strpbrk(owner[["ptr"]],set); if(p) return p - tonumber(owner[["ptr"]]) + 1; }; indexOf = function(p,f){ if(!#p) return; var r = strstr(topointer(owner[["ptr"]],f?f-1),p ); if(r) return ( r - tonumber(owner[["ptr"]]) ) + 1; }; eachIndexOf = function(p){ var i,n=0,#p+1; return function(){ i = owner.indexOf(p,i+n); return i; },owner; }; tokenize = function(delimit){ if(!delimit) delimit='\t\n\r\v\x20'; var tok; var addr = tonumber(owner[["ptr"]]); strtok(addr,delimit) return function(){ var tok = strtok( ,delimit) if(tok) return tok-addr + 1; } }; trim = function(set){ owner.trimleft(); owner.trimright(); return owner; }; trimleft = function(set){ if(!set) set='\t\n\r\v\x20'; var s = tonumber(owner[["ptr"]]); var pl = ::Msvcrt.strspn(s,set); if(pl == 0) return owner; var size = owner.size()-pl; ::Kernel32.RtlMoveMemory(s,s+pl,size); owner.resize(size); return owner; }; trimright = function(set){ if(!set) set='\t\n\r\v\x20'; var size,c = 0; var indexAny = ..string.indexAny; for(i=owner.size();1;-1){ c = convert(owner[["ptr"]],{BYTE v},i-1).v; if !indexAny(set,c ){ size = i;break ; } } owner.resize(size); return owner; }; appendf = function(f,...){ var l1,c = sizeof(owner[["ptr"]]); var l = snprintf( topointer(owner[["ptr"]],l1),c-l1,f,...); while(l<0){ c = c + #f + 1024; owner[["ptr"]] = ..raw.realloc(c,owner[["ptr"]]); l = snprintf( topointer(owner[["ptr"]],l1),c-l1,f,...); } convert( {INT size = l1+l},topointer(owner[["ptr"]],-4)); return owner; }; _get = function(k){ var t = type(k); var size = sizeof(owner[["ptr"]]); if(t==="string") return owner@[[k]] if(t=='number') if(k && k>=1 && k<=size) return convert(owner[["ptr"]],{BYTE v},k-1).v; if(t=='table' && ..table.isArray(k)){ return owner.sliceString(k[1],k[2]) } }; _set = function(k,v){ var t = type(k); var size = sizeof(owner[["ptr"]]); if( t=='number'){ if(k && k>=1 && k<=size) convert({BYTE v = v},owner[["ptr"]],k-1);} elseif(t=='table' && ..table.isArray(k)){ var i,j = ..math.validateRange(sizeof(owner[["ptr"]]),k[1],k[2]) if(i)owner.splice(i,j-i+1,v); } }; append = function(s,l){ owner[["ptr"]] = concat(owner[["ptr"]],s,l); return owner; }; sliceString = function(i,j){ var p = owner[["ptr"]] var s = sizeof(p); i,j = ..math.validateRange(s,i,j); return i?getString(p,i,j):""; } sliceBuffer = function(i,j){ var p = owner[["ptr"]] if(i) return ..raw.slice(p,i,j,sizeof(p)); } sliceArray = function(i,j){ var p = owner[["ptr"]] if(i) return ..raw.slice(p,i,j,sizeof(p)); } leftString = function(len){ if(len<0) owner.sliceString(1,len); return len?owner.sliceString(1,len):"";; } rightString = function(len){ if(len<0) owner.sliceString(len); return len?owner.sliceString(-len):"";; } splice = function(i,l=0,...){ var p = owner[["ptr"]]; var s = sizeof(p); var r = s-(i+l-1) var o; if(l) o = getString(p,i,i+l-1); r = r>0 ?..raw.buffer(r,topointer(p,s-r)):null; owner.resize(i-(l?1:0)); //p=null; if(...)owner.write(...); if(r)owner.write(r); return o; } writeAt = function(i,...){ if(i===null) return owner.write(...); var s1 = sizeof(owner[["ptr"]]); if(i<1) i=1; owner.resize(i-1); owner.write(...); var s2 = sizeof(owner[["ptr"]]); if(s2s) break r[#r+1] = getString(owner[["ptr"]],b,b+v-1); b=b+v; } elseif(v[["_struct"]]){ n = ..raw.sizeof(v); if(b+n-1>s) break; r[#r+1] = ..raw.convert(p,v,b-1); b=b+n } } return ..table.unpack(r); } write = function(s,...){ if(!(type.isString(s)||s[["_struct"]])){ if(s===null) return; s = tostring(s); } owner[["ptr"]] = concat(owner[["ptr"]],s); if(...!==null) return owner.write(...); }; fromUtf16 = function(l,codepage){ if(l===null) l = -1; return ..string.fromUtf16(owner[["ptr"]],codepage,l); } toUtf16 = function(l,codepage){ if(l===null) l = -1; return ..string.toUtf16(owner[["ptr"]],codepage,l); } str = function(u,off){ return ..raw.str(owner[["ptr"]],u,off); } string = function(){ return tostring(owner); } pack = function(fmt,arg2,...){ if(!..raw.pack) error("请先导入 raw.pack 库才能使用 pack 函数",2); if(..table.isArrayLike(arg2)){ for(i=1;#arg2){ owner.pack(fmt,..table.unpack(arg2[i])); } return owner; } if(!..raw.pack) error("请先导入 raw.pack 库才能使用 pack 函数",2); if(type(fmt)==="string")..raw.pack.into(fmt,owner,,arg2,...); else ..raw.pack.into(arg2,owner,fmt,...); return owner; } hex = function(...){ return ..string.hex(tostring(owner),...); } _add = function(v){ if(!(type.isString(v)||v[["_struct"]])){ if(v===null) return owner; v = tostring(v); } owner[["ptr"]] = concat(owner[["ptr"]],v); return owner; } _concat = function(v){ if(!(type.isString(v)||v[["_struct"]])){ if(v===null) return owner; v = tostring(v); } owner[["ptr"]] = concat(owner[["ptr"]],v); return owner; } _tostring = function(){ var len = sizeof(owner[["ptr"]]); if(len)return getString(owner[["ptr"]],1,len); } _topointer = function(){ return owner[["ptr"]]; } _serialize = function(kernelCall){ if(!kernelCall) return tostring(owner); return "null"; } buffer = lambda () ..raw.buffer(sizeof(owner[["ptr"]]),owner[["ptr"]]); free = function(){ owner[["ptr"]] = ..raw.realloc(0,owner[["ptr"]]); }; }; with _meta{ subString = sliceString;//@Deprecated } } /**details(使用说明) string.builder 对象基于 raw.realloc 分配的内存动态指针, 在需要动态改变内存大小时可以提供更好的性能。 string.builder 对象支持用单索引读写字节码(单项索引不可使用负数,1 为起始索引,索引超出范围时忽略或返回 null ), 也支持使用多项索引读写指定范围的数据,读取时返回字符串,可写入的类型有字符串、buffer、结构体。 例如 `stringBuilder[2,4] = "写入数据" //第一个索引不要加负号` 注意在 aardio 中多项索引的第一个索引如果包含操作符(例如负号)要写为空格分格的双层中括号, 例如 `stringBuilder[ [-4,-2] ] = "写入数据" //可用以负数表示自右侧倒计数位置` 这是因为多项索引实际是纯数组的一个语法糖,但如果第一个索引出现操作符 aardio 会将其视为单项索引。 string.builder 在使用下标读写单字节码时效率不及字符串、字节串等基础类型。 但使用多项索引读写片段使用原生内存指针操作,效率与操作字节串差不多。 string.builder 传入原生 API 参数或 raw.toPointer 时可自动转换为指针。 但触发 string.builder 对象修改长度的操作有可能会改变对象内部存储的指针地址, 因此不可存储其指针值,在 string.builder 修改内容或大小后不可再使用之前获得的指针。 end details**/ /**intellisense() string.builder = 字符串生成器。\n创建的对象内部使用动态指针,并负责自动释放动态指针。\n此对象适合用于读写需要原地修改内存、频繁调整内存大小的字节序列。\n与 buffer 不同的是 string.builder 是长度可变的。\n与字符串不同的是 string.builder 是可写可原地修改的。\n\nstring.builder 对象可用相关成员函数或 tostring 转换为字符串,\n也可以在原生 API 或 raw.toPointer 函数中自动转换为普通指针。\n\n要切记任何可能导致自动调整内存大小的操作都有可能改变内部的动态指针地址,\n因此不可记录获得的指针值,总是通过 string.builder 对象自动获取最新指针值。 string.builder() = 创建字符串生成器,不允许跨线程传递此对象。\n可选在参数中指定预分配内存大小。\n生成器对象可使用 `+` 或 `++` 操作符连接字符串、buffer、结构体,或使用`[]`操作符取字节码,\n此对象可用于拼接字符串,并自动调整内存大小,\n\n在大字符串频繁拼接时可明显提升速度,\n小字符串少量拼接不需要使用此对象\n!stdStringBuilder. string.builder.is(__) = 参数 @1 是否 string.builder 对象。 end intellisense**/ /**intellisense(!stdStringBuilder) buffer() = 转换为 buffer 对象(字节串)。 string() = 返回二进制字符串,等价于将当前对象作为参数调用 tostring 函数。\n如果要获取纯文本字符串可改用 str 函数,纯文本以空字符(NUL)为终止符。 str( = 转换为纯文本字符串\n如果需要转换为二进制字符串,直接使用 tostring 函数转换 string.builder 对象即可。 str(.(是否unicode,偏移) = 去掉尾部多余终结符转换为纯文本字符串,参数可省略,偏移默认为 0。\n参数 @1 为 true 反回字符串标记会设置 UTF-16 标记,否则设为 UTF-8 标记 toUtf16(.(转换字节数,源编码) = 转换并返回 UTF16 编码字符串。\n字节数可省略,默认为 -1。\n字节数为 -1 时表示查找 `'\0'` 终止符自动获取长度 fromUtf16(.(转换字符数,目标编码) = 自UTF16编码转换为多字节编码字符串,默认为 UTF-8。\n参数 @1 以字符计数,即 2 个字节为一个单位,字符数只能为数值,省略时默认值为 -1。\n字符数为 -1 表示查找 `'\u0000'` 终止符获取可打印文本长度。 free() = 释放内存。\n此函数设置内部动态指针值为null\n释放动态指针以后,仍然可以调用reserve函数重新分配内存 empty() = 存储内容是否为空。 capacity() = 返回预分配内存大小。\n释放指针后此函数返回0 reserve(__) = 调整预分配内存大小,返回自身。\n小于存储内容大小时则设为存储内容大小。\n为 0 时设为 1024。\n此函数可能改变内部动态指针值\n\n释放动态指针以后,仍然可以调用此函数重新分配内存 reserve() = !stdStringBuilder. size() = 返回存储内容大小。\n释放指针后此函数返回 0。 resize(__) = 调整存储内容大小。\n增加大小时可能改变内部动态指针值。\n减小大小或者设为 0 时仅改变存储的有效数据长度,不会重新分配内存也不会实际修改内存数据。 assign(__) = 重置初始值\n参数可以是字符串,buffer,或结构体\n此函数可能改变内部动态指针值 leftString(__) = 从左侧截取参数指定长度的字符串。\n返回字符串对象,参数可用负数表示右侧截取位置\n不会改变内部动态指针值 rightString(__) = 从右侧截取参数指定长度的字符串。\n返回字符串对象,参数可用负数表示左侧截取位置 \n不会改变内部动态指针值 trimleft(__) = 清除左侧空白字符。\n可选用一个字符串指定要清除的单字节字符。 trimright(__) = 清除右侧空白字符。\n可选用一个字符串指定要清除的单字节字符。 trim(__) = 清除两侧空白字符\n可选用一个字符串指定要清除的单字节字符。 indexOf(.(查找内容,查找开始位置) = 普通字符串查找。\n不使用模式匹配语法。 indexAny("__") = 参数用一个字符串指定要查找的单字节字符。\n返回任意字符最初出现的位置,找不到返回值为空 appendf(.("C格式化串",->->->) = 使用 printf 函数格式化文本。\n此函数可能改变内部动态指针值 append(.(追加数据,追加长度) = 追加数据可以是字符串、buffer、结构体、其他指针。\n如果参数不是指针追加长度可省略。\n此函数可能改变内部动态指针值。 read(.(readSizeOrStruct,->->->) = 从头读取一个或多个内存数据,可指定一个或多个读取参数。\n参数如果是指定读取长度的数值则读取指定长度数据到 buffer 对象,并增加一个返回值。\n参数如果是负数则跳过指定长度(取绝对值)然后继续读取下一个参数。\n参数如果是结构体则读取数据到结构体,并增加一个返回值返回该结构体。\n参数为其他类型时跳过。\n无法满足参数要求的读取长度时停止读取并返回已读取的数据。\n\n此函数返回一个或多个已读取的值。\n每次调用此函数总是从头开始读取。 readAt(.(startIndex,readSizeOrStruct,->->->) = 从参数 @startIndex 指定的位置开始读取一个或多个内存数据,可增加一个或多个读取参数。\n参数 @2 开始如果遇到指定读取长度的数值则读取指定长度数据到 buffer 对象,并增加一个返回值。\n如果遇到指定负数值的参数则跳过指定长度(取绝对值)然后继续读取下一个参数。\n如果遇到结构体参数则读取数据到结构体,并增加一个返回值返回该结构体。\n参数为其他类型时跳过。\n无法满足参数要求的读取长度时停止读取并返回已读取的数据。\n\n此函数返回一个或多个已读取的值。\n每次调用此函数总是从头开始读取。 write(__) = 在尾部写入一个或多个字符串、buffer、结构体参数。\n其他类型参数转换为字符串写入,遇到 null 值停止写入。 writeAt(.(startIndex,arg1->->->) = 在参数 @startIndex 指定位置开始写入一个或多个字符串、buffer、结构体参数。\n其他类型参数转换为字符串写入,遇到 null 值停止写入。\n\n@startIndex 为 1 表示从开头写入,\n省略 @startIndex 在尾部追加式写入(等价于调用 write 函数)。\n\n与此函数相比,splice 方法可指定要删除的旧数据长度再插入新数据,后面的数据自动向后移动。\n在 writeAt 则是在指定位置开始覆写而非插入,会覆盖已存在的内容,不会移动旧数据的位置。\n相同的是 write,writeAt,splice 方法都可以在内存不够时自动增大内存以容纳更长的内容。 eachIndexOf(查找内容) = @for(i in ??.eachIndexOf(__/*查找内容*/)){ __/*迭代变量 i 为当前找到子串的开始索引*/ } tokenize(分割符) = @for(i in ??.eachIndexOf("__/*单字节分割符*/")){ __/*迭代变量 i 为当前分隔符所在索引*/ } sliceString(.(startIndex,endIndex) = 读取指定索引范围的字符串。\n省略 startIndex 默认为 1,省略 endIndex 默认为最后一个字节。\n可用负数表示自右侧倒计数的索引。\n \nstartIndex 小于 1 时会设为 1,endIndex 大于长度时会设为长度。\n如果 endIndex 小于 startIndex 则返回 null 。 sliceBuffer(.(startIndex,endIndex) = 读取指定索引范围的字节串到 buffer 对象。\n省略 startIndex 默认为 1,省略 endIndex 默认为最后一个字节。\n可用负数表示自右侧倒计数的索引。\n \nstartIndex 小于 1 时会设为 1,endIndex 大于长度时会设为长度。\n如果 endIndex 小于 startIndex 则返回 null 。 sliceArray(.(startIndex,endIndex) = 读取指定索引范围的字节串到数组对象。\n省略 startIndex 默认为 1,省略 endIndex 默认为最后一个字节。\n可用负数表示自右侧倒计数的索引。\n \nstartIndex 小于 1 时会设为 1,endIndex 大于长度时会设为长度。\n如果 endIndex 小于 startIndex 则返回 null 。 splice(.(start,deleteCount,arg1,->->->) = 自参数 @start 指定索引删除 @deleteCount 长度(默认为 0)的字节,\n然后在删除位置写入后续的任意个参数,可写入字符串、buffer、结构体。\n如果删除了数据则返回包含删除数据的字符串对象。 find( = 查找字符串,用法与 string.find 相同 find(.("查找模式串",开始位置) = 使用模式串在字符串中查找子串的位置,\n开始位置为可选参数,默认为1,必须以字节计数,\n函数返回匹配结果的起始,结束位置,以及所有捕获分组\n如果有多个捕获分组,将附加为多个返回值 match( = 匹配字符串,用法与 string.match 相同 match(.("查找模式串",开始位置) = 使用模式串在字符串中查找子串。\n参数 @2 可选指定起始位置,负数表示尾部倒计数。\n返回匹配字符串,如果使用了匹配分组则返回多个对应的匹配串。\n返回值的顺序对应模式串中左圆括号的开始顺序 replace( = 使用模式匹配查找并替换字符串。\n此 string.replace 的基本用法相同,\n但此函数修改自身并返回自身(可能会触发内存调整并且变更内部存储的动态指针)。\n第二个返回值为替换次数。 replace(.("字符串","模式串",替换字符串,替换次数) = 使用模式匹配在字符串中查找替换。\n替换字符串,可使用 `\1` 至`\9`引用匹配分组,`\0`表示匹配到的完整字符\n\n替换次数省略则全局替换\n返回值为替换后的新字符串 replace(.("字符串","模式串",替换表,替换次数) = 使用模式匹配在字符串中查找替换。\n替换表对象中键为匹配到的字符串,替换值可以是字符串、数值、函数、false\n其中数值转换为字符串返回,false 表示取消替换,\n函数用于接收匹配结果并返回新字符串,有几个匹配分组就有几个回调参数 replace(.("字符串","模式串",替换函数,替换次数) = 使用模式匹配在字符串中查找替换。\n替换回调函数返回需要替换的新字符串,不返回值则保留原字符串。\n有几个匹配分组就有几个回调参数。\n与 string.replace 不同的是,这里回调函数的 owner 参数为 null 值。\n\n替换次数省略则全局替换\n返回值为替换后的新字符串 replace(.("字符串","@查找字符串","替换字符串",替换次数) = 禁用模式匹配替换,\n在查找串中用模式匹配语法,替换字符串反斜杠也仅仅表示字面值不再表示匹配分组,\n以`@@`开始则是`<@@...@>`的缩写形式,忽略大小写查找\n替换次数省略则全局替换 pack( = 调用 raw.pack 打包二进制数据并写入当前对象。\n可连续写入,或批量写入二维数组。\n必须提前导入 raw.pack 库。 pack(.(format,->->->) = 参数 @format 指定二进制格式化字符串(格式请参考 raw.pack 库)。\n其他不定个数的参数指定待打包的值,也可以传入包含多个参数的单个数组参数。\n成功返回字节串( buffer 类型 ),\n失败返回 null,错误信息。\n\n为了与 Python 的用法保持一致,\n每个打包解包的参数都必须是单个非数组值(数值、字符串、buffer 等)。\n但改用 getStruct 或 getCode 可以得到支持数组的 aardio 结构体声明。\n将结构体作为参数就以用 raw.convert 解包或调用 raw.buffer 或 raw.tostring 打包。 pack(.(format,argsArray) = 参数 @format 指定二进制格式化字符串(格式请参考 raw.pack 库)。\n参数 @argsArray 指定一个二维数组则批量执行打包操作,每个数组项指定一组打包参数。\n此函数遍历数组并连续调用 pack 方法在当前对象尾部追加打包后的二进制数据。 pack() = !stdStringBuilder. hex(.(前缀,忽略字符串) = 返回十六进制编码后的字符串,所有参数可省略。\n调用参数将被传递给 string.hex 函数,用法请参考 string.hex 说明。 end intellisense**/