diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/LICENSE new file mode 100644 index 0000000..95fddb1 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 LiteLoaderQQNT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/README.md new file mode 100644 index 0000000..374f6f0 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/README.md @@ -0,0 +1,6 @@ +# LiteLoaderQQNT + +LiteLoaderQQNT 是 QQNT 的插件加载器,一般在 QQNT 的环境内简称为 LiteLoader。 +它可以让你自由地为 QQNT 添加各种插件,并实现例如美化主题、增加功能等各种功能。 + +详情查看 LiteLoaderQQNT 官网:https://liteloaderqqnt.github.io diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/package.json new file mode 100644 index 0000000..c67e6bb --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/package.json @@ -0,0 +1,18 @@ +{ + "name": "liteloader-qqnt", + "version": "1.1.1", + "description": "QQNT 插件加载器 LiteLoaderQQNT —— 轻量 · 简洁 · 开源", + "main": "./src/init.js", + "private": true, + "license": "MIT", + "author": { + "name": "LiteLoaderQQNT", + "url": "https://github.com/LiteLoaderQQNT" + }, + "homepage": "https://liteloaderqqnt.github.io", + "bugs": "https://github.com/LiteLoaderQQNT/LiteLoaderQQNT/issues", + "repository": { + "type": "git", + "url": "https://github.com/LiteLoaderQQNT/LiteLoaderQQNT.git" + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/assets/icon.svg b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/assets/icon.svg new file mode 100644 index 0000000..b160759 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/assets/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/assets/list-item.html b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/assets/list-item.html new file mode 100644 index 0000000..fa499f3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/assets/list-item.html @@ -0,0 +1,141 @@ + + +
+
+ +
+ + 插件名 + (未激活) + (<4) + + + 插件描述 + +
+
+ +
+ 版本:版本 + + 开发: + 类型: + 平台: + 依赖: +
+
+
+
+ 更新 + 安装 + 卸载 + 重试 + 详情 +
+
\ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/assets/view.html b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/assets/view.html new file mode 100644 index 0000000..924cc37 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/assets/view.html @@ -0,0 +1,136 @@ + + + + + + + + +
+ + 插件总数: + + + 刷新会禁用缓存并请求,所以会比较慢,可用来检查更新,建议不要频繁刷新 + +
+
+ 刷新 +
+
+ +
+ 是否使用镜像 + 设置系统代理时建议关闭 +
+
+ + + 修改镜像 + +
+
+
+
+
+ + + + + + + +
+ 选择包来源 + +
+
+
注意:安装/更新 时会删除插件原文件(不包括配置),如果有修改过的文件,请先备份或者使用 git 进行安装/更新
+
+
+ 使用仓库代码包 +
+
+ 使用release包 +
+
+
+ + +
+ 标题 + +
+
+
+
content
+
+
+
+ 确认 + 取消 +
+
\ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/main/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/main/index.js new file mode 100644 index 0000000..9921f0d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/main/index.js @@ -0,0 +1,5 @@ +"use strict";const v=require("electron"),I=require("fs"),T=require("path"),ce=require("util"),ae=require("events"),he=require("zlib"),fe=require("stream"),le=require("http"),Ee=require("https");function ue(a){return a&&a.__esModule&&Object.prototype.hasOwnProperty.call(a,"default")?a.default:a}/** + * @license node-stream-zip | (c) 2020 Antelle | https://github.com/antelle/node-stream-zip/blob/master/LICENSE + * Portions copyright https://github.com/cthackers/adm-zip | https://raw.githubusercontent.com/cthackers/adm-zip/master/LICENSE + */let D=I;const de=ce,y=T,M=ae,$=he,W=fe,s={LOCHDR:30,LOCSIG:67324752,LOCVER:4,LOCFLG:6,LOCHOW:8,LOCTIM:10,LOCCRC:14,LOCSIZ:18,LOCLEN:22,LOCNAM:26,LOCEXT:28,EXTSIG:134695760,EXTHDR:16,EXTCRC:4,EXTSIZ:8,EXTLEN:12,CENHDR:46,CENSIG:33639248,CENVEM:4,CENVER:6,CENFLG:8,CENHOW:10,CENTIM:12,CENCRC:16,CENSIZ:20,CENLEN:24,CENNAM:28,CENEXT:30,CENCOM:32,CENDSK:34,CENATT:36,CENATX:38,CENOFF:42,ENDHDR:22,ENDSIG:101010256,ENDSIGFIRST:80,ENDSUB:8,ENDTOT:10,ENDSIZ:12,ENDOFF:16,ENDCOM:20,MAXFILECOMMENT:65535,ENDL64HDR:20,ENDL64SIG:117853008,ENDL64SIGFIRST:80,ENDL64OFS:8,END64HDR:56,END64SIG:101075792,END64SIGFIRST:80,END64SUB:24,END64TOT:32,END64SIZ:40,END64OFF:48,STORED:0,SHRUNK:1,REDUCED1:2,REDUCED2:3,REDUCED3:4,REDUCED4:5,IMPLODED:6,DEFLATED:8,ENHANCED_DEFLATED:9,PKWARE:10,BZIP2:12,LZMA:14,IBM_TERSE:18,IBM_LZ77:19,FLG_ENC:0,FLG_COMP1:1,FLG_COMP2:2,FLG_DESC:4,FLG_ENH:8,FLG_STR:16,FLG_LNG:1024,FLG_MSK:4096,FLG_ENTRY_ENC:1,EF_ID:0,EF_SIZE:2,ID_ZIP64:1,ID_AVINFO:7,ID_PFS:8,ID_OS2:9,ID_NTFS:10,ID_OPENVMS:12,ID_UNIX:13,ID_FORK:14,ID_PATCH:15,ID_X509_PKCS7:20,ID_X509_CERTID_F:21,ID_X509_CERTID_C:22,ID_STRONGENC:23,ID_RECORD_MGT:24,ID_X509_PKCS7_RL:25,ID_IBM1:101,ID_IBM2:102,ID_POSZIP:18064,EF_ZIP64_OR_32:4294967295,EF_ZIP64_OR_16:65535},R=function(a){let e,i,r,t,h,d;const u=this,N=a.storeEntries!==!1?{}:null,Q=a.file,ee=a.nameEncoding?new TextDecoder(a.nameEncoding):null;te();function te(){a.fd?(e=a.fd,B()):D.open(Q,"r",(n,o)=>{if(n)return u.emit("error",n);e=o,B()})}function B(){D.fstat(e,(n,o)=>{if(n)return u.emit("error",n);i=o.size,r=a.chunkSize||Math.round(i/1e3),r=Math.max(Math.min(r,Math.min(128*1024,i)),Math.min(1024,i)),ie()})}function z(n,o){if(n||!o)return u.emit("error",n||new Error("Archive read error"));let f=t.lastPos,c=f-t.win.position;const E=t.win.buffer,l=t.minPos;for(;--f>=l&&--c>=0;)if(E.length-c>=4&&E[c]===t.firstByte&&E.readUInt32LE(c)===t.sig){t.lastBufferPosition=c,t.lastBytesRead=o,t.complete();return}if(f===l)return u.emit("error",new Error("Bad archive"));if(t.lastPos=f+1,t.chunkSize*=2,f<=l)return u.emit("error",new Error("Bad archive"));const p=Math.min(t.chunkSize,f-l);t.win.expandLeft(p,z)}function ie(){const n=Math.min(s.ENDHDR+s.MAXFILECOMMENT,i);t={win:new q(e),totalReadLength:n,minPos:i-n,lastPos:i,chunkSize:Math.min(1024,r),firstByte:s.ENDSIGFIRST,sig:s.ENDSIG,complete:re},t.win.read(i-t.chunkSize,t.chunkSize,z)}function re(){const n=t.win.buffer,o=t.lastBufferPosition;try{h=new pe,h.read(n.slice(o,o+s.ENDHDR)),h.headerOffset=t.win.position+o,h.commentLength?u.comment=n.slice(o+s.ENDHDR,o+s.ENDHDR+h.commentLength).toString():u.comment=null,u.entriesCount=h.volumeEntries,u.centralDirectory=h,h.volumeEntries===s.EF_ZIP64_OR_16&&h.totalEntries===s.EF_ZIP64_OR_16||h.size===s.EF_ZIP64_OR_32||h.offset===s.EF_ZIP64_OR_32?ne():(t={},Z())}catch(f){u.emit("error",f)}}function ne(){const n=s.ENDL64HDR;t.lastBufferPosition>n?(t.lastBufferPosition-=n,H()):(t={win:t.win,totalReadLength:n,minPos:t.win.position-n,lastPos:t.win.position,chunkSize:t.chunkSize,firstByte:s.ENDL64SIGFIRST,sig:s.ENDL64SIG,complete:H},t.win.read(t.lastPos-t.chunkSize,t.chunkSize,z))}function H(){const n=t.win.buffer,o=new me;o.read(n.slice(t.lastBufferPosition,t.lastBufferPosition+s.ENDL64HDR));const f=i-o.headerOffset;t={win:t.win,totalReadLength:f,minPos:o.headerOffset,lastPos:t.lastPos,chunkSize:t.chunkSize,firstByte:s.END64SIGFIRST,sig:s.END64SIG,complete:se},t.win.read(i-t.chunkSize,t.chunkSize,z)}function se(){const n=t.win.buffer,o=new Le;o.read(n.slice(t.lastBufferPosition,t.lastBufferPosition+s.END64HDR)),u.centralDirectory.volumeEntries=o.volumeEntries,u.centralDirectory.totalEntries=o.totalEntries,u.centralDirectory.size=o.size,u.centralDirectory.offset=o.offset,u.entriesCount=o.volumeEntries,t={},Z()}function Z(){t={win:new q(e),pos:h.offset,chunkSize:r,entriesLeft:h.volumeEntries},t.win.read(t.pos,Math.min(r,i-t.pos),G)}function G(n,o){if(n||!o)return u.emit("error",n||new Error("Entries read error"));let f=t.pos-t.win.position,c=t.entry;const E=t.win.buffer,l=E.length;try{for(;t.entriesLeft>0;){c||(c=new Ie,c.readHeader(E,f),c.headerOffset=t.win.position+f,t.entry=c,t.pos+=s.CENHDR,f+=s.CENHDR);const p=c.fnameLen+c.extraLen+c.comLen,m=p+(t.entriesLeft>1?s.CENHDR:0);if(l-f{if(f)return o(f);const E=k(c);let l=new De(e,E,c.compressedSize);if(c.method!==s.STORED)if(c.method===s.DEFLATED)l=l.pipe($.createInflateRaw());else return o(new Error("Unknown compression method: "+c.method));A(c)&&(l=l.pipe(new Ce(l,c.crc,c.size))),o(null,l)},!1)},this.entryDataSync=function(n){let o=null;if(this.openEntry(n,(c,E)=>{o=c,n=E},!0),o)throw o;let f=Buffer.alloc(n.compressedSize);if(new _(e,f,0,n.compressedSize,k(n),c=>{o=c}).read(!0),o)throw o;if(n.method!==s.STORED)if(n.method===s.DEFLATED||n.method===s.ENHANCED_DEFLATED)f=$.inflateRawSync(f);else throw new Error("Unknown compression method: "+n.method);if(f.length!==n.size)throw new Error("Invalid size");return A(n)&&new F(n.crc,n.size).data(f),f},this.openEntry=function(n,o,f){if(typeof n=="string"&&(b(),n=N[n],!n))return o(new Error("Entry not found"));if(!n.isFile)return o(new Error("Entry is not file"));if(!e)return o(new Error("Archive closed"));const c=Buffer.alloc(s.LOCHDR);new _(e,c,0,c.length,n.offset,E=>{if(E)return o(E);let l;try{n.readDataHeader(c),n.encrypted&&(l=new Error("Entry encrypted"))}catch(p){l=p}o(l,n)}).read(f)};function k(n){return n.offset+s.LOCHDR+n.fnameLen+n.extraLen}function A(n){return(n.flags&8)!==8}function U(n,o,f){u.stream(n,(c,E)=>{if(c)f(c);else{let l,p;E.on("error",m=>{p=m,l&&(E.unpipe(l),l.close(()=>{f(m)}))}),D.open(o,"w",(m,w)=>{if(m)return f(m);if(p){D.close(e,()=>{f(p)});return}l=D.createWriteStream(o,{fd:w}),l.on("finish",()=>{u.emit("extract",n,o),p||f()}),E.pipe(l)})}})}function j(n,o,f){if(!o.length)return f();let c=o.shift();c=y.join(n,y.join(...c)),D.mkdir(c,{recursive:!0},E=>{if(E&&E.code!=="EEXIST")return f(E);j(n,o,f)})}function P(n,o,f,c,E){if(!f.length)return c(null,E);const l=f.shift(),p=y.join(n,l.name.replace(o,""));U(l,p,m=>{if(m)return c(m,E);P(n,o,f,c,E+1)})}this.extract=function(n,o,f){let c=n||"";if(typeof n=="string"&&(n=this.entry(n),n?c=n.name:c.length&&c[c.length-1]!=="/"&&(c+="/")),!n||n.isDirectory){const E=[],l=[],p={};for(const m in N)if(Object.prototype.hasOwnProperty.call(N,m)&&m.lastIndexOf(c,0)===0){let w=m.replace(c,"");const X=N[m];if(X.isFile&&(E.push(X),w=y.dirname(w)),w&&!p[w]&&w!=="."){p[w]=!0;let S=w.split("/").filter(x=>x);for(S.length&&l.push(S);S.length>1;){S=S.slice(0,S.length-1);const x=S.join("/");if(p[x]||x===".")break;p[x]=!0,l.push(S)}}}l.sort((m,w)=>m.length-w.length),l.length?j(o,l,m=>{m?f(m):P(o,c,E,f,0)}):P(o,c,E,f,0)}else D.stat(o,(E,l)=>{l&&l.isDirectory()?U(n,y.join(o,y.basename(n.name)),f):U(n,o,f)})},this.close=function(n){d||!e?(d=!0,n&&n()):(d=!0,D.close(e,o=>{e=null,n&&n(o)}))};const oe=M.EventEmitter.prototype.emit;this.emit=function(...n){if(!d)return oe.call(this,...n)}};R.setFs=function(a){D=a};R.debugLog=(...a)=>{R.debug&&console.log(...a)};de.inherits(R,M.EventEmitter);const O=Symbol("zip");R.async=class extends M.EventEmitter{constructor(e){super();const i=new R(e);i.on("entry",r=>this.emit("entry",r)),i.on("extract",(r,t)=>this.emit("extract",r,t)),this[O]=new Promise((r,t)=>{i.on("ready",()=>{i.removeListener("error",t),r(i)}),i.on("error",t)})}get entriesCount(){return this[O].then(e=>e.entriesCount)}get comment(){return this[O].then(e=>e.comment)}async entry(e){return(await this[O]).entry(e)}async entries(){return(await this[O]).entries()}async stream(e){const i=await this[O];return new Promise((r,t)=>{i.stream(e,(h,d)=>{h?t(h):r(d)})})}async entryData(e){const i=await this.stream(e);return new Promise((r,t)=>{const h=[];i.on("data",d=>h.push(d)),i.on("end",()=>{r(Buffer.concat(h))}),i.on("error",d=>{i.removeAllListeners("end"),t(d)})})}async extract(e,i){const r=await this[O];return new Promise((t,h)=>{r.extract(e,i,(d,L)=>{d?h(d):t(L)})})}async close(){const e=await this[O];return new Promise((i,r)=>{e.close(t=>{t?r(t):i()})})}};class pe{read(e){if(e.length!==s.ENDHDR||e.readUInt32LE(0)!==s.ENDSIG)throw new Error("Invalid central directory");this.volumeEntries=e.readUInt16LE(s.ENDSUB),this.totalEntries=e.readUInt16LE(s.ENDTOT),this.size=e.readUInt32LE(s.ENDSIZ),this.offset=e.readUInt32LE(s.ENDOFF),this.commentLength=e.readUInt16LE(s.ENDCOM)}}class me{read(e){if(e.length!==s.ENDL64HDR||e.readUInt32LE(0)!==s.ENDL64SIG)throw new Error("Invalid zip64 central directory locator");this.headerOffset=g(e,s.ENDSUB)}}class Le{read(e){if(e.length!==s.END64HDR||e.readUInt32LE(0)!==s.END64SIG)throw new Error("Invalid central directory");this.volumeEntries=g(e,s.END64SUB),this.totalEntries=g(e,s.END64TOT),this.size=g(e,s.END64SIZ),this.offset=g(e,s.END64OFF)}}class Ie{readHeader(e,i){if(e.length=8&&this.size===s.EF_ZIP64_OR_32&&(this.size=g(e,i),i+=8,r-=8),r>=8&&this.compressedSize===s.EF_ZIP64_OR_32&&(this.compressedSize=g(e,i),i+=8,r-=8),r>=8&&this.offset===s.EF_ZIP64_OR_32&&(this.offset=g(e,i),i+=8,r-=8),r>=4&&this.diskStart===s.EF_ZIP64_OR_16&&(this.diskStart=e.readUInt32LE(i))}get encrypted(){return(this.flags&s.FLG_ENTRY_ENC)===s.FLG_ENTRY_ENC}get isFile(){return!this.isDirectory}}class _{constructor(e,i,r,t,h,d){this.fd=e,this.buffer=i,this.offset=r,this.length=t,this.position=h,this.callback=d,this.bytesRead=0,this.waiting=!1}read(e){R.debugLog("read",this.position,this.bytesRead,this.length,this.offset),this.waiting=!0;let i;if(e){let r=0;try{r=D.readSync(this.fd,this.buffer,this.offset+this.bytesRead,this.length-this.bytesRead,this.position+this.bytesRead)}catch(t){i=t}this.readCallback(e,i,i?r:null)}else D.read(this.fd,this.buffer,this.offset+this.bytesRead,this.length-this.bytesRead,this.position+this.bytesRead,this.readCallback.bind(this,e))}readCallback(e,i,r){if(typeof r=="number"&&(this.bytesRead+=r),i||!r||this.bytesRead===this.length)return this.waiting=!1,this.callback(i,this.bytesRead);this.read(e)}}class q{constructor(e){this.position=0,this.buffer=Buffer.alloc(0),this.fd=e,this.fsOp=null}checkOp(){if(this.fsOp&&this.fsOp.waiting)throw new Error("Operation in progress")}read(e,i,r){this.checkOp(),this.buffer.length{this.emit("error",t)})}_transform(e,i,r){let t;try{this.verify.data(e)}catch(h){t=h}r(t,e)}}class F{constructor(e,i){this.crc=e,this.size=i,this.state={crc:-1,size:0}}data(e){const i=F.getCrcTable();let r=this.state.crc,t=0,h=e.length;for(;--h>=0;)r=i[(r^e[t++])&255]^r>>>8;if(this.state.crc=r,this.state.size+=e.length,this.state.size>=this.size){const d=Buffer.alloc(4);if(d.writeInt32LE(~this.state.crc&4294967295,0),r=d.readUInt32LE(0),r!==this.crc)throw new Error("Invalid CRC");if(this.state.size!==this.size)throw new Error("Invalid size")}}static getCrcTable(){let e=F.crcTable;if(!e){F.crcTable=e=[];const i=Buffer.alloc(4);for(let r=0;r<256;r++){let t=r;for(let h=8;--h>=0;)t&1?t=3988292384^t>>>1:t=t>>>1;t<0&&(i.writeInt32LE(t,0),t=i.readUInt32LE(0)),e[r]=t}}return e}}function V(a,e){const i=K(a,16),r=K(e,16),t={h:parseInt(i.slice(0,5).join(""),2),m:parseInt(i.slice(5,11).join(""),2),s:parseInt(i.slice(11,16).join(""),2)*2,Y:parseInt(r.slice(0,7).join(""),2)+1980,M:parseInt(r.slice(7,11).join(""),2),D:parseInt(r.slice(11,16).join(""),2)},h=[t.Y,t.M,t.D].join("-")+" "+[t.h,t.m,t.s].join(":")+" GMT+0";return new Date(h).getTime()}function K(a,e){let i=(a>>>0).toString(2);for(;i.lengthv.ipcMain.on("LiteLoader.ListViewer."+a,e),Y=(a,e)=>v.ipcMain.handle("LiteLoader.ListViewer."+a,e);Oe("log",(a,e)=>{C(e)});Y("getPkg",async(a,e,i)=>(C("安装",e,i),await J(i).then(r=>{C("下载完成",e);const t=T.join(LiteLoader.plugins[Se].path.data,`${e}.zip`);return I.writeFileSync(t,r.data),C("写入",t),ge(t,e)}).catch(r=>{throw new Error(`${r.message} +${i}`)})));Y("removePkg",async(a,e,i=!1)=>{C("卸载",e);let r,t;if(LiteLoader.plugins[e])({plugin:r,data:t}=LiteLoader.plugins[e].path);else{if(C("未激活的插件,寻找路径",e),r=Re(e),!r)return{success:!1,message:"未找到插件路径"};C("寻找到的路径",r)}try{return i&&t&&I.rmdirSync(t,{recursive:!0}),I.rmdirSync(r,{recursive:!0}),C("卸载完成",e),{success:!0}}catch(h){return{success:!1,message:h.message}}});function C(...a){console.log("\x1B[32m[ListViewer]\x1B[0m",...a)}function J(a){return new Promise((e,i)=>{const t=(a.startsWith("https")?Ee:le).get(a);t.on("error",i),t.on("response",h=>{if(h.statusCode&&h.statusCode>=300&&h.statusCode<400)return e(J(h.headers.location));const d=[];h.on("error",L=>i(L)),h.on("data",L=>d.push(L)),h.on("end",()=>{const L=Buffer.concat(d);e({data:L,str:L.toString("utf-8"),url:h.url})})})})}async function ge(a,e){const{plugins:i}=LiteLoader.path,r=LiteLoader.plugins[e]?.path?.plugin||T.join(i,e);try{I.existsSync(r)&&I.rmSync(r,{recursive:!0,force:!0}),I.mkdirSync(r,{recursive:!0}),C("开始解压",a);const t=new Ne.async({file:a}),h=await t.entries(),d=!Object.hasOwn(h,"manifest.json");for(const L of Object.values(h))if(!L.name.includes(".github")){const u=`${r}/${d?L.name.split("/").slice(1).join("/"):L.name}`;if(L.isDirectory){I.mkdirSync(u,{recursive:!0});continue}try{if(L.isFile){await t.extract(L.name,u);continue}}catch{I.mkdirSync(u.slice(0,u.lastIndexOf("/")),{recursive:!0}),await t.extract(L.name,u);continue}}return await t.close(),C("解压完成",a),I.rmSync(a,{force:!0}),C("删除完成",a),{success:!0}}catch(t){return v.dialog.showErrorBox("插件列表查看",t.stack||t.message),I.rmSync(r,{recursive:!0,force:!0}),t.message.includes("Bad archive")?{success:!1,message:"安装包异常"}:{success:!1,message:"安装失败 "+t.message}}}function Re(a){const e=I.readdirSync(LiteLoader.path.plugins).map(r=>T.join(LiteLoader.path.plugins,r)),i=Object.values(LiteLoader.plugins).map(r=>r.path.plugin);return e.filter(r=>!i.includes(r)).find(r=>{try{if(JSON.parse(I.readFileSync(T.join(r,"manifest.json")).toString()).slug===a)return!0}catch(t){C("findManifest",t)}return!1})} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/manifest.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/manifest.json new file mode 100644 index 0000000..d699bd3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/manifest.json @@ -0,0 +1,31 @@ +{ + "$schema": "./manifest_schema.json", + "manifest_version": 4, + "type": "extension", + "name": "插件列表查看", + "slug": "list-viewer", + "description": "插件列表查看·安装·更新", + "icon": "assets/icon.svg", + "thumb": "assets/icon.svg", + "version": "1.2.1", + "authors": [ + { + "name": "ltxhhz", + "link": "https://github.com/ltxhhz" + } + ], + "platform": [ + "win32", + "linux", + "darwin" + ], + "injects": { + "main": "./main/index.js", + "preload": "./preload/index.js", + "renderer": "./renderer/index.js" + }, + "repository": { + "repo": "ltxhhz/LL-plugin-list-viewer", + "branch": "master" + } +} \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/preload/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/preload/index.js new file mode 100644 index 0000000..118b6f8 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/preload/index.js @@ -0,0 +1 @@ +"use strict";const r=require("electron"),t=r.contextBridge.exposeInMainWorld;t("ListViewer",{getPkg:(e,i)=>r.ipcRenderer.invoke("LiteLoader.ListViewer.getPkg",e,i),removePkg:e=>r.ipcRenderer.invoke("LiteLoader.ListViewer.removePkg",e),log:(...e)=>r.ipcRenderer.send("LiteLoader.ListViewer.removePkg",...e)}); diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/renderer/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/renderer/index.js new file mode 100644 index 0000000..703a760 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/list-viewer/renderer/index.js @@ -0,0 +1,857 @@ +const semver = /^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i; +const validateAndParse = (version) => { + if (typeof version !== "string") { + throw new TypeError("Invalid argument expected string"); + } + const match = version.match(semver); + if (!match) { + throw new Error(`Invalid argument not valid semver ('${version}' received)`); + } + match.shift(); + return match; +}; +const isWildcard = (s) => s === "*" || s === "x" || s === "X"; +const tryParse = (v) => { + const n = parseInt(v, 10); + return isNaN(n) ? v : n; +}; +const forceType = (a, b) => typeof a !== typeof b ? [String(a), String(b)] : [a, b]; +const compareStrings = (a, b) => { + if (isWildcard(a) || isWildcard(b)) + return 0; + const [ap, bp] = forceType(tryParse(a), tryParse(b)); + if (ap > bp) + return 1; + if (ap < bp) + return -1; + return 0; +}; +const compareSegments = (a, b) => { + for (let i = 0; i < Math.max(a.length, b.length); i++) { + const r = compareStrings(a[i] || "0", b[i] || "0"); + if (r !== 0) + return r; + } + return 0; +}; +const compareVersions = (v1, v2) => { + const n1 = validateAndParse(v1); + const n2 = validateAndParse(v2); + const p1 = n1.pop(); + const p2 = n2.pop(); + const r = compareSegments(n1, n2); + if (r !== 0) + return r; + if (p1 && p2) { + return compareSegments(p1.split("."), p2.split(".")); + } else if (p1 || p2) { + return p1 ? -1 : 1; + } + return 0; +}; +const compare = (v1, v2, operator) => { + assertValidOperator(operator); + const res = compareVersions(v1, v2); + return operatorResMap[operator].includes(res); +}; +const operatorResMap = { + ">": [1], + ">=": [0, 1], + "=": [0], + "<=": [-1, 0], + "<": [-1], + "!=": [-1, 1] +}; +const allowedOperators = Object.keys(operatorResMap); +const assertValidOperator = (op) => { + if (typeof op !== "string") { + throw new TypeError(`Invalid operator type, expected string but got ${typeof op}`); + } + if (allowedOperators.indexOf(op) === -1) { + throw new Error(`Invalid operator, expected one of ${allowedOperators.join("|")}`); + } +}; +class Node { + value; + next; + constructor(value) { + this.value = value; + } +} +class Queue { + #head; + #tail; + #size; + constructor() { + this.clear(); + } + enqueue(value) { + const node = new Node(value); + if (this.#head) { + this.#tail.next = node; + this.#tail = node; + } else { + this.#head = node; + this.#tail = node; + } + this.#size++; + } + dequeue() { + const current = this.#head; + if (!current) { + return; + } + this.#head = this.#head.next; + this.#size--; + return current.value; + } + clear() { + this.#head = void 0; + this.#tail = void 0; + this.#size = 0; + } + get size() { + return this.#size; + } + *[Symbol.iterator]() { + let current = this.#head; + while (current) { + yield current.value; + current = current.next; + } + } +} +const AsyncResource = { + bind(fn, _type, thisArg) { + return fn.bind(thisArg); + } +}; +function pLimit(concurrency) { + if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) { + throw new TypeError("Expected `concurrency` to be a number from 1 and up"); + } + const queue = new Queue(); + let activeCount = 0; + const next = () => { + activeCount--; + if (queue.size > 0) { + queue.dequeue()(); + } + }; + const run = async (function_, resolve, arguments_) => { + activeCount++; + const result = (async () => function_(...arguments_))(); + resolve(result); + try { + await result; + } catch { + } + next(); + }; + const enqueue = (function_, resolve, arguments_) => { + queue.enqueue( + AsyncResource.bind(run.bind(void 0, function_, resolve, arguments_)) + ); + (async () => { + await Promise.resolve(); + if (activeCount < concurrency && queue.size > 0) { + queue.dequeue()(); + } + })(); + }; + const generator = (function_, ...arguments_) => new Promise((resolve) => { + enqueue(function_, resolve, arguments_); + }); + Object.defineProperties(generator, { + activeCount: { + get: () => activeCount + }, + pendingCount: { + get: () => queue.size + }, + clearQueue: { + value() { + queue.clear(); + } + } + }); + return generator; +} +const hostReg = /^https?:\/\/[^/]+/; +const originMirrors = ["https://mirror.ghproxy.com/", "https://ghproxy.net/", "https://github.moeyy.xyz/"]; +const thisSlug = "list-viewer"; +let config; +async function initConfig() { + const defaultConfig = { + inactivePlugins: [], + debug: false, + useMirror: true, + mirrors: { + downloadUrl: ["https://cdn.jsdelivr.net/gh"] + // rawUrl: [] + } + }; + config = await LiteLoader.api.config.get(thisSlug, defaultConfig); + const save = debounce((obj) => { + LiteLoader.api.config.set(thisSlug, obj); + }, 1e3); + config = deepWatch(config, () => { + save(config); + }); +} +function getRandomItem(arr) { + return arr ? arr[Math.floor(Math.random() * arr.length)] : void 0; +} +function useMirror(url2, mirror, removeHost = true) { + if (/\/gh$/.test(mirror) && mirror.includes("jsdelivr")) { + return mirror + url2.replace(hostReg, "").replace("/raw/", "@").replace(/@v(?!v)/, "@vv"); + } else { + return mirror + (removeHost ? url2.replace(hostReg, "") : url2); + } +} +function localFetch(path, plugin = "list-viewer") { + return fetch(`local:///${LiteLoader.plugins[plugin].path.plugin.replace(":\\", "://").replaceAll("\\", "/")}/${path.startsWith("/") ? path.slice(1) : path}`); +} +function fetchWithTimeout(url2, options, timeout = 3e3) { + config.debug && console.log("fetchWithTimeout", url2); + return new Promise((resolve, reject) => { + const controller = new AbortController(); + const timeoutId = setTimeout(() => { + controller.abort(); + reject(new Error("请求超时")); + }, timeout); + fetch(url2, { ...options, signal: controller.signal }).then((response) => { + clearTimeout(timeoutId); + resolve(response); + }).catch((error) => { + clearTimeout(timeoutId); + reject(error); + }); + }); +} +function debounce(func, delay) { + let timeoutId; + return function(...args) { + if (timeoutId) { + clearTimeout(timeoutId); + } + timeoutId = setTimeout(() => { + func(args); + }, delay); + }; +} +function deepWatch(obj, callback) { + const observer = new Proxy(obj, { + set(target, key, value, receiver) { + const oldValue = target[key]; + if (oldValue !== value) { + callback(); + if (typeof value === "object" && value !== null && !Array.isArray(value)) { + deepWatch(value, callback); + } + } + return Reflect.set(target, key, value, receiver); + } + }); + return observer; +} +const listUrl = { + repo: "LiteLoaderQQNT/Plugin-List", + branch: "v4", + file: "plugins.json" +}; +const defaultIcon = "local://root/src/setting/static/default.png"; +const domParser = new DOMParser(); +const typeMap = { + extension: "扩展", + theme: "主题", + framework: "框架" +}; +let pluginList; +let currentItem; +let currentManifest; +let showDialog; +let filterInput; +function onSettingWindowCreated(view) { + initConfig(); + localFetch("/assets/view.html").then((e) => e.text()).then(async (res) => { + const doms = domParser.parseFromString(res, "text/html"); + filterInput = doms.querySelector("#list-search"); + const refreshBtn = doms.querySelector(".refresh-btn"); + const totalEl = doms.querySelector(".total-text"); + const dialogInstall = doms.querySelector(".list-dialog-install"); + const dialogInstallClose = doms.querySelector(".list-dialog-install-btn-close"); + let resFunc; + dialogInstallClose.addEventListener("click", () => { + dialogInstall.close(); + resFunc(); + }); + const useRepo = doms.querySelector(".list-dialog-btn-repo"); + useRepo.addEventListener("click", () => { + dialogInstall.close(); + resFunc(false); + }); + const useRelease = doms.querySelector(".list-dialog-btn-release"); + useRelease.addEventListener("click", () => { + dialogInstall.close(); + resFunc(true); + }); + let dialogResolve; + const dialog = doms.querySelector(".list-dialog"); + const dialogClose = doms.querySelector(".list-dialog-btn-close"); + dialogClose.addEventListener("click", () => { + dialog.close(); + dialogResolve(); + }); + const dialogTitle = doms.querySelector(".list-dialog-title"); + const dialogContent = doms.querySelector(".list-dialog-content"); + const dialogConfirm = doms.querySelector(".list-dialog-btn-confirm"); + dialogConfirm.addEventListener("click", () => { + dialog.close(); + dialogResolve(true); + }); + const dialogCancel = doms.querySelector(".list-dialog-btn-cancel"); + dialogCancel.addEventListener("click", () => { + dialog.close(); + dialogResolve(false); + }); + showDialog = (option) => { + let dialogInput; + dialogTitle.innerText = option.title; + if (option.type === "confirm" || option.type === "message") { + if (option.message) { + dialogContent.innerText = option.message; + } + if (option.content) { + if (typeof option.content === "string") { + dialogContent.innerHTML = option.content; + } else { + dialogContent.replaceChildren(option.content); + } + } + if (option.type === "message") { + dialogCancel.style.display = "none"; + } + } else if (option.type === "prompt") { + dialogInput = option.textarea ? document.createElement("textarea") : document.createElement("input"); + dialogInput.placeholder = option.placeholder ?? "请输入内容"; + dialogInput.value = option.default ?? ""; + dialogInput.style.width = "100%"; + dialogInput.style.background = "var(--background_02)"; + dialogInput.style.color = "var(--bg_white_light)"; + dialogInput.style.marginTop = "10px"; + dialogContent.replaceChildren(option.message || "", dialogInput); + } + dialogConfirm.innerText = option.confirm || "确定"; + dialogCancel.innerText = option.cancel || "取消"; + return new Promise((resolve) => { + dialog.showModal(); + dialogResolve = (bool) => { + if (option.type === "prompt") { + resolve(bool ? dialogInput.value : ""); + } else { + resolve(bool); + } + }; + }); + }; + const mirrorSwitch = doms.querySelector(".mirror-switch"); + mirrorSwitch.toggleAttribute("is-active", config.useMirror); + mirrorSwitch.onclick = () => { + const isActive = mirrorSwitch.hasAttribute("is-active"); + mirrorSwitch.toggleAttribute("is-active", !isActive); + config.useMirror = !isActive; + }; + const mirrorAddBtn = doms.querySelector(".mirror-add-btn"); + mirrorAddBtn.onclick = () => { + showDialog({ + title: "添加镜像", + type: "prompt", + placeholder: "请输入镜像地址,每行一个", + message: `请输入镜像地址,每行一个,如果代理方式是完整url在地址后面,比如 +https://mirror/https://github.com/xx/xx,则需要写 +https://mirror/https://github.com/ +如果代理方式是 path在地址后面,比如 +https://mirror/xx/xx,则需要写https://mirror +jsdelivr镜像直接按默认那个写就行 +内置三个镜像'https://mirror.ghproxy.com', 'https://ghproxy.net', 'https://github.moeyy.xyz' +使用时默认优先使用第一个,如果没有响应才会使用其他镜像`, + textarea: true, + default: config.mirrors.downloadUrl.join("\n") + }).then((res2) => { + if (res2) { + config.mirrors.downloadUrl = res2.split("\n"); + } + }); + }; + doms.body.childNodes.forEach((dom) => { + view.appendChild(dom); + }); + const showInstallDialog = () => new Promise((resolve) => { + dialogInstall.showModal(); + resFunc = resolve; + }); + createItemComponent(await localFetch("/assets/list-item.html").then((e) => e.text()), showInstallDialog); + const pluginListDom = view.querySelector("#plugin-list"); + const getList1 = (noCache = false) => { + refreshBtn.setAttribute("is-disabled", ""); + getList(noCache).then(async (list) => { + pluginList = list; + totalEl.innerText = list.length.toString(); + const promArr = []; + const limit = pLimit(3); + list.forEach((plugin, i) => { + const dom = document.createElement("plugin-item"); + pluginListDom.appendChild(dom); + promArr.push( + limit(async () => { + const manifest = await getManifest(plugin, noCache); + dom.dataset.index = i + ""; + config.debug && console.log(plugin, manifest); + updateElProp(dom, manifest, plugin.repo); + }) + ); + }); + return Promise.all(promArr); + }).finally(() => { + refreshBtn.removeAttribute("is-disabled"); + }); + }; + refreshBtn.addEventListener("click", () => { + pluginListDom.replaceChildren(); + getList1(true); + }); + getList1(); + }).catch(console.error); +} +function createItemComponent(innerHtml, showInstallDialog) { + class PluginListClass extends HTMLElement { + titleEl; + descriptionEl; + versionEl; + authorsEl; + manipulateEl; + iconEl; + updateBtnEl; + installBtnEl; + uninstallBtnEl; + detailBtnEl; + retryBtnEl; + typeEl; + dependenciesItemsEl; + platformsEl; + manifest = null; + #initPromise; + #initPromiseResolve; + #initialized = false; + constructor() { + super(); + const shadow = this.attachShadow({ mode: "open" }); + shadow.innerHTML = innerHtml; + this.#initPromise = new Promise((resolve) => { + this.#initPromiseResolve = resolve; + if (this.#initialized) + resolve(); + }); + } + connectedCallback() { + this.titleEl = this.shadowRoot.querySelector(".title"); + this.descriptionEl = this.shadowRoot.querySelector(".description"); + this.versionEl = this.shadowRoot.querySelector(".version"); + this.authorsEl = this.shadowRoot.querySelector(".authors"); + this.manipulateEl = this.shadowRoot.querySelector(".manipulate"); + this.iconEl = this.shadowRoot.querySelector(".icon"); + this.typeEl = this.shadowRoot.querySelector(".type"); + this.dependenciesItemsEl = this.shadowRoot.querySelector(".dependencies>.items"); + this.platformsEl = this.shadowRoot.querySelector(".platforms"); + this.updateBtnEl = this.shadowRoot.querySelector(".update"); + const installEvent = async (update = false) => { + currentItem = pluginList[Number(this.dataset.index)]; + currentManifest = this.manifest; + showInstallDialog().then((res) => { + if (res !== void 0) { + if (update) { + this.updateBtnEl.setAttribute("is-disabled", ""); + this.updateBtnEl.innerText = "安装中..."; + } else { + this.installBtnEl.setAttribute("is-disabled", ""); + this.installBtnEl.innerText = "安装中..."; + } + install(res).then((res2) => { + if (res2.success) { + this.dataset.installed = "1"; + this.dataset.inactive = "1"; + if (update) + delete this.dataset.update; + config.inactivePlugins.push(this.manifest.slug); + this.updateOpenDirEvent(); + } else { + showDialog({ title: "安装失败", message: res2.message, type: "message" }); + } + }).catch((e) => { + showDialog({ title: "安装失败", message: e.message, type: "message" }); + }).finally(() => { + if (update) { + this.updateBtnEl.removeAttribute("is-disabled"); + this.updateBtnEl.innerText = "更新"; + } else { + this.installBtnEl.removeAttribute("is-disabled"); + this.installBtnEl.innerText = "安装"; + } + }); + } + }); + }; + this.updateBtnEl.addEventListener("click", () => installEvent(true)); + this.installBtnEl = this.shadowRoot.querySelector(".install"); + this.installBtnEl.addEventListener("click", () => installEvent()); + this.uninstallBtnEl = this.shadowRoot.querySelector(".uninstall"); + this.uninstallBtnEl.addEventListener("click", async () => { + config.debug && console.log("uninstall", this.manifest.name); + currentItem = pluginList[Number(this.dataset.index)]; + currentManifest = this.manifest; + showDialog({ title: "卸载", message: `确定要卸载插件 ${this.manifest.name} 吗?`, type: "confirm" }).then((e) => { + if (e) { + this.uninstallBtnEl.innerText = "卸载中..."; + this.uninstallBtnEl.setAttribute("is-disabled", ""); + uninstall().then((res) => { + if (res.success) { + this.uninstallBtnEl.innerText = "卸载"; + this.uninstallBtnEl.removeAttribute("is-disabled"); + delete this.dataset.installed; + if (config.inactivePlugins.includes(this.manifest.slug)) { + this.dataset.inactive = "0"; + config.inactivePlugins = config.inactivePlugins.filter((e2) => e2 !== this.manifest.slug); + } + this.updateOpenDirEvent(); + } else { + showDialog({ title: "卸载失败", message: res.message, type: "message" }); + } + }); + } + }); + }); + this.retryBtnEl = this.shadowRoot.querySelector(".retry"); + this.retryBtnEl.addEventListener("click", async () => { + this.retryBtnEl.innerText = "重试中..."; + this.retryBtnEl.setAttribute("is-disabled", ""); + const manifest = await getManifest(pluginList[Number(this.dataset.index)]); + this.manifest = manifest; + updateElProp(this, manifest, this.dataset.failed); + this.retryBtnEl.innerText = "重试"; + this.retryBtnEl.removeAttribute("is-disabled"); + }); + this.detailBtnEl = this.shadowRoot.querySelector(".detail"); + this.detailBtnEl.addEventListener("click", async () => { + LiteLoader.api.openExternal(`https://github.com/${pluginList[Number(this.dataset.index)].repo}/tree/${pluginList[Number(this.dataset.index)].branch}`); + }); + filterInput.addEventListener("input", () => this.updateHidden()); + this.updateHidden(); + this.updateOpenDirEvent(); + this.#initialized = true; + this.#initPromiseResolve?.(); + } + static get observedAttributes() { + return ["data-name", "data-version", "data-description", "data-authors", "data-icon", "data-failed", "data-type", "data-dependencies", "data-platforms"]; + } + attributeChangedCallback(name, _oldValue, newValue) { + this.#initPromise.then(() => { + try { + switch (name) { + case "data-failed": + if (newValue) { + this.titleEl.innerText = newValue; + this.titleEl.style.color = "red"; + this.descriptionEl.innerText = "获取失败"; + } else { + this.titleEl.style.removeProperty("color"); + } + break; + case "data-name": + this.titleEl.innerText = newValue || "插件名"; + this.titleEl.title = newValue || ""; + break; + case "data-version": + this.versionEl.innerText = newValue || "版本"; + this.versionEl.title = newValue || ""; + break; + case "data-description": + this.descriptionEl.innerText = newValue || "插件描述"; + this.descriptionEl.title = newValue || ""; + break; + case "data-authors": { + const arr = newValue === "1" ? this.manifest.authors : []; + this.authorsEl.append( + ...arr.map((author) => { + const a = document.createElement("a"); + a.title = author.link; + a.innerText = author.name; + a.onclick = () => LiteLoader.api.openExternal(author.link); + return a; + }).reduce((p, v, i) => { + p[i * 2] = v; + if (i) + p[i * 2 - 1] = " | "; + return p; + }, []) + ); + break; + } + case "data-type": + this.typeEl.innerText = typeMap[newValue + ""] || "unknown"; + break; + case "data-platforms": + this.platformsEl.title = this.platformsEl.innerText = newValue || ""; + break; + case "data-dependencies": { + const arr = newValue === "1" ? this.manifest.dependencies : []; + this.dependenciesItemsEl.append( + ...arr.map((e) => { + const a = document.createElement("a"); + a.title = e; + a.innerText = e; + a.onclick = () => { + const item = document.getElementById(`item-${e}`); + if (item) { + item.scrollIntoView?.(); + item.classList.add("highlight-item"); + setTimeout(() => { + item.classList.remove("highlight-item"); + }, 2e3); + } + }; + return a; + }).reduce((p, v, i) => { + p[i * 2] = v; + if (i) + p[i * 2 - 1] = " | "; + return p; + }, []) + ); + break; + } + case "data-icon": { + const [src, src1] = (newValue || "").split(","); + this.iconEl.src = src || defaultIcon; + let num = 0; + this.iconEl.addEventListener("error", () => { + if (src1 && num < 3) { + const iconPath = this.manifest.icon.replace(/^\.?\//, ""); + switch (num) { + case 0: + this.iconEl.src = src.replace(iconPath, `src/${iconPath}`); + break; + case 1: + this.iconEl.src = src1; + break; + case 2: + this.iconEl.src = src1.replace(iconPath, `src/${iconPath}`); + break; + } + } else { + this.iconEl.src = defaultIcon; + } + num++; + }); + break; + } + default: + break; + } + } catch (error) { + console.error(this.dataset.name || this.dataset.failed, error); + } + }); + } + updateOpenDirEvent() { + if (this.manifest && LiteLoader.plugins[this.manifest.slug] && this.dataset.installed === "1") { + this.titleEl.title = "点击打开插件所在目录"; + this.titleEl.addEventListener("click", () => { + LiteLoader.api.openPath(LiteLoader.plugins[this.manifest.slug].path.plugin); + }); + this.titleEl.style.cursor = "pointer"; + } + } + /** + * 过滤用,判断是否应该隐藏 + */ + updateHidden() { + try { + const authors = this.dataset.authors === "1" ? this.manifest.authors : []; + const str = (this.dataset.name || "") + (this.dataset.version || "") + (this.dataset.description || "") + (this.dataset.version || "") + authors.map((e) => e.name).join(""); + if (!filterInput.value || str.toLowerCase().includes(filterInput.value.toLowerCase())) { + this.hidden = false; + } else { + this.hidden = true; + } + } catch (error) { + console.error(this.manifest?.slug || this.dataset.name, error); + } + } + } + customElements.define("plugin-item", PluginListClass); + return new PluginListClass(); +} +function updateElProp(el, manifest, repo) { + if (manifest) { + el.id = `item-${manifest.slug}`; + el.dataset.name = manifest.name; + el.manifest = manifest; + el.updateOpenDirEvent(); + el.dataset.description = manifest.description; + el.dataset.lower4 = Number(manifest.manifest_version) >= 4 ? "" : "1"; + el.dataset.authors = manifest.authors ? "1" : ""; + el.dataset.platforms = manifest.platform.join("|"); + el.dataset.installed = LiteLoader.plugins[manifest.slug] ? "1" : ""; + el.dataset.slug = manifest.slug; + el.dataset.icon = getIconUrls(pluginList[Number(el.dataset.index)], manifest).toString(); + el.dataset.defaultIcon = defaultIcon; + el.dataset.type = manifest.type; + el.dataset.dependencies = manifest.dependencies ? "1" : ""; + delete el.dataset.failed; + if (LiteLoader.plugins[manifest.slug]) { + el.dataset.version = LiteLoader.plugins[manifest.slug].manifest.version; + config.debug && console.log(manifest.slug, LiteLoader.plugins[manifest.slug], manifest); + el.dataset.update = compare(manifest.version, LiteLoader.plugins[manifest.slug]?.manifest?.version ?? manifest.version, ">") ? "1" : ""; + el.shadowRoot.querySelector(".newer-version").innerText = `-> ${manifest.version}`; + } else { + el.dataset.version = manifest.version; + } + if (config.inactivePlugins.includes(manifest.slug)) { + if (!LiteLoader.plugins[manifest.slug]) { + el.dataset.inactive = "1"; + } else { + config.inactivePlugins = config.inactivePlugins.filter((v) => v !== manifest.slug); + } + } + } else { + el.dataset.failed = repo; + } +} +async function getList(noCache = false, again = false) { + let url; + if (config.useMirror) { + const m = getGithubMirror(!again); + url = useMirror(`https://github.com/${listUrl.repo}/raw/${listUrl.branch}/${listUrl.file}`, m || getRandomItem(originMirrors), !!m); + } else { + url = `https://github.com/${listUrl.repo}/raw/${listUrl.branch}/${listUrl.file}`; + } + return await fetchWithTimeout(url, { + cache: noCache ? "no-cache" : "default" + }).then((res) => res.status === 200 ? res.json() : null).catch((err) => { + if (again) { + console.error(`getList ${url}`, err); + return null; + } else { + console.warn(`getList ${url}`, err); + return getList(noCache, true); + } + }); +} +async function getManifest(item, noCache = false, again = false) { + let url; + let m = getGithubMirror(!again); + if (config.useMirror) { + url = useMirror(`https://github.com/${item.repo}/raw/${item.branch}/manifest.json`, m || getRandomItem(originMirrors), !!m); + } else { + url = `https://github.com/${item.repo}/raw/${item.branch}/manifest.json`; + } + return await fetchWithTimeout(url, { + cache: noCache ? "no-cache" : "default" + }).then((res) => { + if (res.status === 200) { + return res.json(); + } else { + m = getGithubMirror(!again); + if (config.useMirror) { + url = useMirror(`https://github.com/${item.repo}/raw/${item.branch}/package.json`, m || getRandomItem(originMirrors), !!m); + } else { + url = `https://github.com/${item.repo}/raw/${item.branch}/package.json`; + } + return fetchWithTimeout(url, { + cache: noCache ? "no-cache" : "default" + }).then(async (res1) => { + if (res1.status === 200) { + const pkg = await res1.json(); + const obj = pkg.liteloader_manifest; + if (obj) { + obj.version = pkg.version; + obj.description = pkg.description; + obj.authors = typeof pkg.author === "string" ? [{ name: pkg.author, link: `https://github.com/${pkg.author}` }] : [pkg.author]; + return obj; + } + } + return null; + }); + } + }).catch((err) => { + if (again) { + console.error(`getManifest ${url}`, err); + return null; + } else { + console.warn(`getManifest ${url}`, err); + return getManifest(item, noCache, true); + } + }); +} +async function install(release = false) { + let url; + config.debug && console.log("install", currentItem); + if (release) { + url = await getLatestReleaseUrl(currentItem); + if (!url) { + return Promise.resolve({ + success: false, + message: "获取release包失败" + }); + } + } else { + url = getArchiveUrl(currentItem); + } + if (config.useMirror) { + const m = getGithubMirror(); + return ListViewer.getPkg(currentManifest.slug, useMirror(url, m || getRandomItem(originMirrors), !!m)); + } else { + return ListViewer.getPkg(currentManifest.slug, url); + } +} +function getIconUrls(item, manifest) { + if (manifest.icon) { + const iconPath = manifest.icon.replace(/^\.?\//, ""); + const m = getGithubMirror(true); + const m1 = getGithubMirror(); + if (config.useMirror) { + return [ + useMirror(`https://github.com/${item.repo}/raw/${item.branch}/${iconPath}`, m || getRandomItem(originMirrors), !!m), + useMirror(`https://github.com/${item.repo}/raw/${item.branch}/${iconPath}`, m1 || getRandomItem(originMirrors), !!m1) + ]; + } else { + return [`https://github.com/${item.repo}/raw/${item.branch}/${iconPath}`]; + } + } + return []; +} +function uninstall() { + return ListViewer.removePkg(currentManifest.slug); +} +function getGithubMirror(first = false) { + if (first) { + return config.mirrors.downloadUrl[0]; + } + return getRandomItem(config.mirrors.downloadUrl.splice(1)); +} +function getArchiveUrl(item) { + return `https://github.com/${item.repo}/archive/refs/heads/${item.branch}.zip`; +} +async function getLatestReleaseUrl(item) { + const url = `https://api.github.com/repos/${item.repo}/releases/latest`; + const body = await fetchWithTimeout(url).then((e) => e.json()).catch((err) => { + throw new Error(`${err.message} +${url}`); + }); + const zipFile = body.assets.find((asset) => asset.name.endsWith(".zip")); + return zipFile.browser_download_url; +} +export { + onSettingWindowCreated +}; diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/LICENSE new file mode 100644 index 0000000..30d007f --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 XiaoHe321 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/README.md new file mode 100644 index 0000000..fb59691 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/README.md @@ -0,0 +1,90 @@ +# LiteLoaderQQNT - anti-recall + +LiteLoaderQQNT插件,用于比较完善的防撤回。 +使用前需要安装[LiteLoaderQQNT](https://github.com/mo-jinran/LiteLoaderQQNT),并在QQNT新版上使用。 + +建议加入更新日志频道,接收最新更新和使用提示[https://t.me/llqqnt_xh](https://t.me/llqqnt_xh)。 + +## 使用方法 + +*建议从`LiteLoaderQQNT`应用商店中直接下载安装,方便快捷。*(新版本1.0`LiteLoaderQQNT`没有插件商店了,请遵循下面的手动安装方法) + +**手动安装**:请前往[版本发布页面](https://github.com/xh321/LiteLoaderQQNT-Anti-Recall/releases)下载最新版压缩包(`qq-anti-recall.zip`),在`LiteLoaderQQNT数据目录/plugins/`下面新建文件夹(名称任意,建议为插件名称),蒋插件压缩包里的内容解压进去,重启QQNT即可。 + +直接克隆源码的话,记得需要手动进行`npm install`。 + +**版本不兼容提示**:从0.2.29起,插件已适配1.0版本以上`LiteLoaderQQNT`框架,同时不再兼容旧版框架,请遵循[安装方法](https://liteloaderqqnt.github.io/guide/install.html)更新框架。 + +**版本提示**:从0.2.35起,自旧版本升级上来的用户会丢失配置和已撤回数据库,需要手动迁移,这是因为之前的数据目录写入错误。迁移过程:把`LiteLoaderQQNT数据目录/qq-recalled-db`文件夹移动到`LiteLoaderQQNT数据目录/anti_recall`文件夹内即可(最终路径:`LiteLoaderQQNT数据目录/anti_recall/qq-recalled-db`。配置文件不建议迁移,可能已经被其他配置文件覆盖,建议重新配置插件(一共也没几个配置项目)。 + +注意:如果你是从旧版更新到新版(1.0)框架后,需要把`LiteLoaderQQNT`数据目录中的plugins_data文件夹重命名为data,否则之前的记录和配置文件会丢失。(如果仍然丢失就没办法了) + + +## 什么情况下反撤回会失效 + +1. QQ接到消息时已经被撤回(只接到了撤回的消息)。 +2. QQ未在线时接到的消息(上线后已经被撤回)。 +3. 从接到某条消息开始到这条消息撤回,还收到了超过`MaxMsgSaveLimit(内存中消息最多缓存条数)`条消息(可以在配置界面里配置)。 +4. 插件出现错误。 + +## 重启QQ反撤回失效? + +请使用命令行运行QQ,并观察日志输出。 + +如果出现:数据库打开失败的提示,并且下面的cause类似于 + +``` +cause: [Error: IO error: LockFile \data\qq-recalled-db/LOCK...] +``` + +可能是你之前的QQ退出不完全,或者你开了两个QQ。 + +请完全关闭QQ进程,并重试。若还不行,可能是数据库文件损坏,考虑清空上面的路径(\data\qq-recalled-db)中的LOCK文件,若还不行,清空所有文件(会导致之前反撤回内容丢失)。 + + + +## 太长不看版 + +在NTQQ打开期间无论怎样操作,**反撤回均能生效**。 + +若开启数据库储存(**默认关闭**,请前往配置界面手动开启),NTQQ重启后可恢复已撤回的消息,目前已测试文本、表情、图片、转发消息均能正常恢复;文件若没下载则不能打开,但还能看见文件消息本身;其他消息类型未测试,不提供保证。 + +若不满意撤回的颜色、阴影和提示,也请前往配置界面手动调整。 + +若反撤回消息太多,累积在数据库里可能造成加载卡顿,现在已经做了优化,一般来说不需要清理,但如果觉得卡顿,如有必要,还是可以**隔一段时间**通过插件配置界面清理一次已储存的消息。 + +## 详细说明 + +所有被撤回的消息会被带上红框,下方会出现“已撤回”的小提示(均可通过配置关闭)。**重新进消息界面已撤回的消息仍然可以正常显示。** + +**使用LevelDB进行数据持久化储存**,默认**不**启用,需要手动去设置里开启。如果不开启,重启QQ消息会丢失。若开启,当前版本重启QQ后**已能恢复文字、图片、转发和表情消息**,其他消息不提供保证,若不能恢复请提`ISSUE`。 + +`LevelDB`有时候启动的时候会加载失败,导致无法加载反撤回数据库内容。如果发现之前的反撤回失效,重启QQ一般都能解决(目前正在尽力避免这种问题)。 + +如果你需要调整撤回样式,请前往插件的设置界面,手动修改调整。 + +不建议手动修改配置文件,修改后不会实时生效(需要重启QQ),建议用配置界面修改。 + + + +现在,即使图片在撤回之前未打开过,撤回后也能继续查看了,并且不止缩略图,点开看也是可以的!即使图片显示为灰色的损坏样式,你也可以双击试试能不能打开看。 + +但是如果不是在眼前撤回的,而是撤回后你才点进聊天界面去看的,图片仍然会转圈一段时间(请耐心等待),猜测因为QQ会先检查图片是否存在,撤回后还没下载当然不存在,所以转圈;等插件下载好图片后,需要等待QQ重新检查,才能显示图片。 + +图片如果撤回太快,反撤回后有可能会一直加载不出来,这个可能是图片已经从服务器上删除了,这种情况下,**无法恢复**。 + +## 原理介绍 + +**之前版本(过时):** 为什么在简介里说是简易的呢?因为这个防撤回原理是通过拦截撤回消息的IPC,这样消息就不会被删除。可是,若重新打开消息界面,NTQQ似乎会重新向逻辑层拉取一次消息(旧版QQ的防撤回直接从通信上就拦截了撤回包,所以没有这个问题;而本插件仅拦截到了渲染层的IPC,逻辑层还是一个黑盒),所以导致消息仍然被撤回了。 + +**现在版本的变更:** 在接到消息后会将消息存于内存中,进入消息界面后,若发现撤回会拦截撤回包,并显示红色边框以及给文字附加删除线。若重新进入消息页面,会遍历消息列表,若发现撤回的提示,会寻找之前保存过的消息,若找到,则将撤回提示替换为之前保存的消息,并显示红色边框以及给文字附加删除线;若未找到,则仍然显示撤回提示。这样,重进消息界面**仍然可以正确反撤回**(包括打开独立聊天界面**也可以**)。 + +- 为避免太占内存,内存中缓存消息的上限默认为10000条。如有需要,可前往插件设置界面修改。这个值的意义是,假设默认最多存10000条,那么总接到消息的10000条之前的消息若被撤回则无法恢复。**不过,若一条消息在撤回后你回去看了,并且反撤回生效(也就是只要你看到了这条消息出现红框和删除线),则这条消息会被额外储存到专门的“已撤回消息数组”中,(若开启数据库,也会同时存进数据库),这个数组没有容量上限,不会被本限制所约束。** +- 为避免可能存在的并发问题,消息超过上述限制后,每次接到消息后默认会删除内存缓存的消息中的前500条,直到低于上述上限。这个500条你也可以通过插件设置页面来变更。建议修改为你平均每秒接到的消息数量。 +- 若开启数据库储存功能,则任何撤回的消息都会存入本地的数据库(位于LLQQNT的插件数据目录(`plugin_data`里),以便于重启后读取已撤回的消息记录。数据库明文储存(后续会推出加密功能),请谨慎保管。 + +相关详细的原理和说明请参阅源码。 + +## 协议及免责 + +MIT | 禁止用于任何非法用途,插件开发属学习与研究目的,仅自用,未提供给任何第三方使用。任何不当使用导致的任何侵权问题责任自负。 diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/icon.png b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/icon.png new file mode 100644 index 0000000..e873d1f Binary files /dev/null and b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/icon.png differ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/main.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/main.js new file mode 100644 index 0000000..8147a01 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/main.js @@ -0,0 +1,682 @@ +const http = require("http"); +const https = require("https"); +const fs = require("fs"); +const path = require("path"); +const { app, ipcMain, dialog } = require("electron"); + +var configFilePath = ""; +var pluginDataDir = path.join(LiteLoader.path.data, "anti_recall"); + +const { Level } = require("level"); +var db = null; + +var sampleConfig = { + mainColor: "#ff6d6d", + saveDb: false, + enableShadow: true, + enableTip: true, + isAntiRecallSelfMsg: false, + maxMsgSaveLimit: 10000, + deleteMsgCountPerTime: 500, +}; + +var nowConfig = {}; + +function initConfig() { + fs.writeFileSync( + configFilePath, + JSON.stringify(sampleConfig, null, 2), + "utf-8" + ); +} + +function loadConfig() { + if (!fs.existsSync(configFilePath)) { + initConfig(); + return sampleConfig; + } else { + return JSON.parse(fs.readFileSync(configFilePath, "utf-8")); + } +} + +onLoad(); + +async function onLoad() { + if (!fs.existsSync(pluginDataDir)) { + fs.mkdirSync(pluginDataDir, { recursive: true }); + } + configFilePath = path.join(pluginDataDir, "config.json"); + nowConfig = loadConfig(); + + if (nowConfig.mainColor == null) { + nowConfig.mainColor = "#ff6d6d"; + } + if (nowConfig.enableShadow == null) { + nowConfig.enableShadow = true; + } + if (nowConfig.enableTip == null) { + nowConfig.enableTip = true; + } + fs.writeFileSync(configFilePath, JSON.stringify(nowConfig, null, 2), "utf-8"); + + ipcMain.handle( + "LiteLoader.anti_recall.getNowConfig", + async (event, message) => { + return nowConfig; + } + ); + + ipcMain.handle("LiteLoader.anti_recall.saveConfig", async (event, config) => { + nowConfig = config; + sendChatWindowsMessage("LiteLoader.anti_recall.mainWindow.repatchCss"); + fs.writeFileSync(configFilePath, JSON.stringify(config, null, 2), "utf-8"); + }); + + if (nowConfig.saveDb) { + db = new Level(path.join(pluginDataDir, "qq-recalled-db"), { + valueEncoding: "json", + }); + + db.open((e) => { + if (e !== undefined && e !== null) { + app.whenReady().then(() => { + dialog + .showMessageBox({ + type: "warning", + title: "警告", + message: + "打开反撤回数据库失败,可能是上次QQ进程未完全退出或同时开启了多个QQ(插件不支持QQ多开,如需多开请关闭保存到数据库功能)。建议关闭QQ并彻底结束QQ进程,再重启QQ,否则反撤回消息无法正常保存(即时反撤回仍生效,只是重启QQ后会丢失)。", + buttons: ["继续打开QQ", "关闭QQ"], + }) + .then((r) => { + if (r.response == 1) { + app.exit(); + } + }); + }); + output( + "打开数据库失败,可能是QQ进程未完全退出。请查看下面详细错误信息中的cause部分:", + e + ); + } + }); + } + ipcMain.handle("LiteLoader.anti_recall.clearDb", async (event, message) => { + dialog + .showMessageBox({ + type: "warning", + title: "警告", + message: "清空所有已储存的撤回消息后不可恢复,是否确认清空?", + buttons: ["确定", "取消"], + cancelId: 1, + }) + .then(async (idx) => { + //第一个按钮 + if (idx.response == 0) { + await db.clear(); + dialog.showMessageBox({ + type: "info", + title: "提示", + message: + "清空完毕,之前保存的所有已撤回消息均被删除,重启QQ后就能看见效果。", + buttons: ["确定"], + }); + } + }); + }); + + // 老方法,废弃 + // setTimeout(async () => { + // await loadDb() + // .catch(async (e) => { + // output( + // "Error while loading recalled msgs from db: " + + // e.toString(), + // ", retrying..." + // ); + // await db.open(); + // await loadDb(); + // }) + // .catch((e) => { + // output( + // "Error while loading recalled msgs from db: " + + // e.toString(), + // ", stop load." + // ); + // }); + // }, 100); + + app.on("quit", async () => { + output("Closing db..."); + await db.close(); + }); +} + +// 废弃 +// async function loadDb() { +// if (nowConfig.saveDb) { +// output("Loading recalled msgs from db..."); +// var counter = 0; +// for await (const value of db.values()) { +// counter++; +// recalledMsg.push(value); +// if (value.msg == null) continue; +// for (item of value.msg.elements) { +// if (item.picElement != null) { +// item.picElement.thumbPath = new Map([ +// [ +// 0, +// item.picElement.sourcePath +// .replace("Ori", "Thumb") +// .replace( +// item.picElement.md5HexStr, +// item.picElement.md5HexStr + "_0" +// ) +// ], +// [ +// 198, +// item.picElement.sourcePath +// .replace("Ori", "Thumb") +// .replace( +// item.picElement.md5HexStr, +// item.picElement.md5HexStr + "_198" +// ) +// ], +// [ +// 720, +// item.picElement.sourcePath +// .replace("Ori", "Thumb") +// .replace( +// item.picElement.md5HexStr, +// item.picElement.md5HexStr + "_720" +// ) +// ] +// ]); +// } +// } +// } +// output(`Loaded ${counter} msgs.`); +// } else { +// output("Db saving is disabled, continue."); +// } +// } + +var msgFlow = []; +var recalledMsg = []; + +function request(url) { + return new Promise((resolve, reject) => { + const protocol = url.startsWith("https") ? https : http; + const req = protocol.get(url); + req.on("error", (error) => reject(error)); + req.on("response", (res) => { + // 发生跳转就继续请求 + if (res.statusCode >= 300 && res.statusCode <= 399) { + return resolve(request(res.headers.location)); + } + const chunks = []; + res.on("error", (error) => reject(error)); + res.on("data", (chunk) => chunks.push(chunk)); + res.on("end", () => resolve(Buffer.concat(chunks))); + }); + }); +} + +// 下载被撤回的图片(抄自Lite-Tools) +async function processPic(msgItem) { + msgItem?.elements?.forEach(async (el) => { + if (el?.picElement) { + const pic = el.picElement; + const picName = pic.md5HexStr.toUpperCase(); + const picUrl = `https://gchat.qpic.cn/gchatpic_new/0/0-0-${picName}/`; + output("Download lost pic(s)... url=", picUrl, "msgId=", msgItem.msgId); + if (!fs.existsSync(pic.sourcePath)) { + output("Download pic:", `${picUrl}0`); + const body = await request(`${picUrl}0`); + fs.mkdirSync(path.dirname(pic.sourcePath), { recursive: true }); + fs.writeFileSync(pic.sourcePath, body); + } else { + output("Pic already existed, skip.", pic.sourcePath); + } + // 修复本地数据中的错误 + if ( + pic?.thumbPath && + (pic.thumbPath instanceof Array || pic.thumbPath instanceof Object) + ) { + pic.thumbPath = new Map([ + [ + 0, + pic.sourcePath + .replace("Ori", "Thumb") + .replace(pic.md5HexStr, pic.md5HexStr + "_0"), + ], + [ + 198, + pic.sourcePath + .replace("Ori", "Thumb") + .replace(pic.md5HexStr, pic.md5HexStr + "_198"), + ], + [ + 720, + pic.sourcePath + .replace("Ori", "Thumb") + .replace(pic.md5HexStr, pic.md5HexStr + "_720"), + ], + ]); + } + if ( + pic?.thumbPath && + (pic.thumbPath instanceof Array || pic.thumbPath instanceof Map) + ) { + pic.thumbPath.forEach(async (el, key) => { + if (!fs.existsSync(el)) { + output("Download thumbs:", `${picUrl}${key}`); + const body = await request(`${picUrl}${key}`); + fs.mkdirSync(path.dirname(el), { recursive: true }); + fs.writeFileSync(el, body); + } + }); + } + } + }); +} + +//废弃(老方法) +// async function downloadPic(msgList) { +// if (msgList == null) return; + +// for (item of msgList.elements) { +// if (item.picElement) { +// var pic = item.picElement; +// var picBasePath = pic.md5HexStr.toUpperCase(); +// var urlBase = `https://gchat.qpic.cn/gchatpic_new/0/0-0-${picBasePath}/`; + +// output( +// `[${pic.md5HexStr +// .toUpperCase() +// .substring(0, 5)}]Downloading lost pic(s)... ${urlBase}` +// ); + +// if (!fs.existsSync(pic.sourcePath)) { +// const body = await request(`${urlBase}0`); +// fs.mkdirSync(path.dirname(pic.sourcePath), { recursive: true }); +// fs.writeFileSync(pic.sourcePath, body); +// } else { +// output( +// `[${pic.md5HexStr.toUpperCase().substring(0, 5)}] ${ +// pic.sourcePath +// } Pic(s) already existed, skip.` +// ); +// } + +// if (pic.thumbPath instanceof Array) { +// pic.thumbPath.forEach(async (value, key) => { +// if (!fs.existsSync(value)) { +// const body = await request(`${urlBase}${key}`); +// fs.mkdirSync(path.dirname(value), { recursive: true }); +// fs.writeFileSync(value, body); +// } +// }); +// } +// output( +// `[${pic.md5HexStr +// .toUpperCase() +// .substring(0, 5)}]Download completed!` +// ); +// } +// } +// } + +function insertDb(msg) { + if (db != null) { + db.get(msg.id, (error, value) => { + if (error.status == 404) { + db.put(msg.id, msg, (err) => { + if (err) throw err; + }); + } else { + if (error) throw error; + } + }); + } +} + +async function getMsgById(id) { + if (db != null) { + try { + return await db.get(id); + } catch (e) { + if (e.status != 404) { + output(e); + } + return null; + } + } else { + output("Warning: Db is null, no previous msg available."); + } + return null; +} + +function sendChatWindowsMessage(message) { + for (var window of mainWindowObjs) { + if (window.isDestroyed()) continue; + window.webContents.send(message); + } +} + +var mainWindowObjs = []; +function onBrowserWindowCreated(window) { + window.webContents.on("did-stop-loading", () => { + //只针对主界面和独立聊天界面生效 + if ( + window.webContents.getURL().indexOf("#/main/message") != -1 || + window.webContents.getURL().indexOf("#/chat") != -1 + ) { + mainWindowObjs.push(window); + // const proxyEvents = new Proxy( + // window.webContents._events["-ipc-message"], + // { + // // 拦截函数调用 + // apply(target, thisArg, argumentsList) { + // if ( + // argumentsList[3][0]["eventName"] && + // !argumentsList[3][0]["eventName"].includes( + // "ns-Logger" + // ) + // ) { + // output(JSON.stringify(argumentsList)); + // } + // return target.apply(thisArg, argumentsList); + // } + // } + // ); + // window.webContents._events["-ipc-message"] = proxyEvents; + + //补充知识: + //撤回的原理是,你先发了一条消息,这条消息有一个msgId,然后又撤回了他,那腾讯就会发一条一样msgId的撤回消息包来替换,这样你以后拉取的话,这个msgId只会对应一条撤回提示了; + //本插件的原理是,先在内存中临时储存所有消息(1000条上限),然后如果有撤回发生,则将撤回的提示替换为之前保存的消息。 + + const original_send = + (window.webContents.__qqntim_original_object && + window.webContents.__qqntim_original_object.send) || + window.webContents.send; + + //var myUid = ""; + const patched_send = async function (channel, ...args) { + // output(channel, JSON.stringify(args)); + // if (db != null) { + // db.put("a", { x: 123 }, function (err) { + // if (err) throw err; + + // db.get("a", function (err, value) { + // console.log(value); // { x: 123 } + // }); + // }); + // } + + try { + if (args.length >= 2) { + //MessageList IPC 中能看到消息全量更新内容,其中包含撤回的提示,但并不包含被撤回的消息(被撤回的已经被替换掉了),需要替换撤回提示为之前保存的消息内容 + if ( + args.some( + (item) => + item && + item.hasOwnProperty("msgList") && + item.msgList != null && + item.msgList instanceof Array && + item.msgList.length > 0 + ) + ) { + var currentMsgPeer = ""; + + //撤回提示所在的msgList下标数组,在后面需要一个个替换为真实的消息 + var needUpdateIdx = []; + for (let idx in args[1].msgList) { + var item = args[1].msgList[idx]; + currentMsgPeer = item.peerUid; + if (item.msgType == 5 && item.subMsgType == 4) { + if ( + item.elements[0].grayTipElement != null && + item.elements[0].grayTipElement.revokeElement != null && + (nowConfig.isAntiRecallSelfMsg || + !item.elements[0].grayTipElement.revokeElement + .isSelfOperate) + ) { + needUpdateIdx.push(idx); + } + } + // console.log( + // item.recallTime, + // "<====>", + // item.elements, + // "<====>", + // item.elements[0].grayTipElement.revokeElement + // ); + } + + needUpdateIdx.sort((a, b) => b - a); + + for (var i of needUpdateIdx) { + var currMsgId = args[1].msgList[i].msgId; + + //如果之前存了消息 + var olderMsg = msgFlow.find((i) => i.id == currMsgId); + var olderMsgFromRecalledMsg = recalledMsg.find( + (i) => i.id == currMsgId + ); + + var dbMsg = await getMsgById(currMsgId); + + //优先从已保存的撤回的消息中获取 + if (olderMsgFromRecalledMsg != null) { + // original_send.call( + // window.webContents, + // channel, + // { + // type: "request", + // eventName: "ns-ntApi-2" + // }, + // [ + // { + // cmdName: + // "nodeIKernelMsgListener/onRecvMsg", + // cmdType: "event", + // payload: { + // msgList: [ + // olderMsgFromRecalledMsg.msg + // ] + // } + // } + // ] + // ); + + await processPic(olderMsgFromRecalledMsg.msg); + + args[1].msgList[i] = olderMsgFromRecalledMsg.msg; + + output( + "Detected recall, intercepted and recovered from old msg" + ); + } + //如果没有存过,则说明他在消息流里 + else if (olderMsg != null) { + args[1].msgList[i] = olderMsg.msg; + + //没专门存过这条消息到专门的反撤回数组中,就存一下 + if (olderMsgFromRecalledMsg == null) { + recalledMsg.push(olderMsg); + } + + await processPic(olderMsg.msg); + + output( + "Detected recall, intercepted and recovered from msgFlow" + ); + } else if (dbMsg != null) { + args[1].msgList[i] = dbMsg.msg; + + //没专门存过这条消息到专门的反撤回数组中,就存一下 + if (olderMsgFromRecalledMsg == null) { + recalledMsg.push(dbMsg); + } + + await processPic(dbMsg.msg); + + output( + "Detected recall, intercepted and recovered from dbMsg" + ); + } + args[1].msgList[i].isOnlineMsg = true; + } + original_send.call( + window.webContents, + "LiteLoader.anti_recall.mainWindow.recallTipList", + recalledMsg + .filter( + (i) => i.sender == currentMsgPeer || i?.sender == null + ) + .map((i) => i.id) + ); + } + + //增量更新 IPC + if ( + args.some( + (item) => + item instanceof Array && + item.length > 0 && + item[0] && + item[0].cmdName != null + ) + ) { + var args1 = args[1][0]; + if (args1 == null) return; + + //方法一:获取个人信息的IPC,用来获取个人UID,避免防撤回自己的消息 + // if (args1.cmdName.indexOf("onProfileDetailInfoChanged") != -1) { + // myUid = args1.payload.info.uid; + // } else + //目前采用方法二,直接获取撤回消息中的参数 + //拦截撤回IPC + if ( + args1.cmdName != null && + (args1.cmdName.indexOf("onMsgInfoListUpdate") != -1 || + args1.cmdName.indexOf("onActiveMsgInfoUpdate") != -1) && + args1.payload != null && + args1.payload.msgList instanceof Array && + args1.payload.msgList[0].msgType == 5 && + args1.payload.msgList[0].subMsgType == 4 + ) { + var msgList = args1.payload.msgList[0]; + + //不是自己撤回的,才拦截 + if ( + msgList.elements[0].grayTipElement != null && + msgList.elements[0].grayTipElement.revokeElement != null && + (nowConfig.isAntiRecallSelfMsg || + !msgList.elements[0].grayTipElement.revokeElement + .isSelfOperate) + ) { + original_send.call( + window.webContents, + "LiteLoader.anti_recall.mainWindow.recallTip", + msgList.msgId + ); + + //如果之前存了消息 + var olderMsg = msgFlow.find((i) => i.id == msgList.msgId); + var olderMsgFromRecalledMsg = recalledMsg.find( + (i) => i.id == msgList.msgId + ); + + //之前存了消息,但是还没有存入专门的反撤回数组中 + if (olderMsg != null && olderMsgFromRecalledMsg == null) { + recalledMsg.push(olderMsg); + if (nowConfig.saveDb) { + insertDb(olderMsg); + } + } + + await processPic(olderMsg?.msg); + await processPic(olderMsgFromRecalledMsg?.msg); + + args[1][0].cmdName = "none"; + args[1][0].payload.msgList.pop(); + + // console.log(args1.payload); + output("Detected recall, intercepted"); + } + } + //接到消息 + else if ( + (args1.cmdName != null && + args1.payload != null && + (args1.cmdName.indexOf("onRecvMsg") != -1 || + args1.cmdName.indexOf("onRecvActiveMsg") != -1) && + args1.payload.msgList instanceof Array) || + (args1.cmdName.indexOf("onAddSendMsg") != -1 && + args1.payload.msgRecord != null) || + (args1.cmdName.indexOf("onMsgInfoListUpdate") != -1 && + args1.payload.msgList instanceof Array) + ) { + var msgList = + args1.payload.msgList instanceof Array + ? args1.payload.msgList + : [args1.payload.msgRecord]; + + for (msg of msgList) { + var msgId = msg.msgId; + + var olderMsgIdx = msgFlow.findIndex((i) => i.id == msgId); + if (olderMsgIdx == -1) { + msgFlow.push({}); + olderMsgIdx = msgFlow.length - 1; + } + msgFlow[olderMsgIdx] = { + id: msgId, + sender: msg.peerUid, + msg: msg, + }; + + if (nowConfig.maxMsgSaveLimit == null) { + nowConfig.maxMsgSaveLimit = 10000; + } + if (nowConfig.deleteMsgCountPerTime == null) { + nowConfig.deleteMsgCountPerTime = 500; + } + + if (msgFlow.length > nowConfig.maxMsgSaveLimit) { + msgFlow.splice(0, nowConfig.deleteMsgCountPerTime); + } + } + } + } + } + } catch (e) { + output( + "NTQQ Anti-Recall Error: ", + e, + "Please report this to https://github.com/xh321/LiteLoaderQQNT-Anti-Recall/issues, thank you" + ); + } + + return original_send.call(window.webContents, channel, ...args); + }; + if (window.webContents.__qqntim_original_object) + window.webContents.__qqntim_original_object.send = patched_send; + else window.webContents.send = patched_send; + + output( + "NTQQ Anti-Recall loaded for window: " + window.webContents.getURL() + ); + } + }); +} + +function output(...args) { + console.log("\x1b[32m%s\x1b[0m", "Anti-Recall:", ...args); +} + +module.exports = { + onBrowserWindowCreated, +}; diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/manifest.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/manifest.json new file mode 100644 index 0000000..5efebfd --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/manifest.json @@ -0,0 +1,29 @@ +{ + "manifest_version": 4, + "type": "extension", + "name": "防撤回", + "slug": "anti_recall", + "description": "防止QQNT撤回消息", + "version": "0.2.40", + "icon": "./icon.png", + "authors": [ + { + "name": "XiaoHe321", + "link": "https://github.com/xh321" + } + ], + "repository": { + "repo": "xh321/LiteLoaderQQNT-Anti-Recall", + "branch": "master", + "release": { + "tag": "0.2.40", + "file": "qq-anti-recall.zip" + } + }, + "platform": ["win32", "darwin", "linux"], + "injects": { + "main": "./main.js", + "preload": "./preload.js", + "renderer": "./renderer.js" + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build new file mode 100644 index 0000000..78d3889 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build @@ -0,0 +1,12 @@ +#!/bin/sh +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") + +case `uname` in + *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;; +esac + +if [ -x "$basedir/node" ]; then + exec "$basedir/node" "$basedir/../node-gyp-build/bin.js" "$@" +else + exec node "$basedir/../node-gyp-build/bin.js" "$@" +fi diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-optional b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-optional new file mode 100644 index 0000000..03297c5 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-optional @@ -0,0 +1,12 @@ +#!/bin/sh +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") + +case `uname` in + *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;; +esac + +if [ -x "$basedir/node" ]; then + exec "$basedir/node" "$basedir/../node-gyp-build/optional.js" "$@" +else + exec node "$basedir/../node-gyp-build/optional.js" "$@" +fi diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-optional.cmd b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-optional.cmd new file mode 100644 index 0000000..74d85f2 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-optional.cmd @@ -0,0 +1,17 @@ +@ECHO off +GOTO start +:find_dp0 +SET dp0=%~dp0 +EXIT /b +:start +SETLOCAL +CALL :find_dp0 + +IF EXIST "%dp0%\node.exe" ( + SET "_prog=%dp0%\node.exe" +) ELSE ( + SET "_prog=node" + SET PATHEXT=%PATHEXT:;.JS;=;% +) + +endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\node-gyp-build\optional.js" %* diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-optional.ps1 b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-optional.ps1 new file mode 100644 index 0000000..45995c3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-optional.ps1 @@ -0,0 +1,28 @@ +#!/usr/bin/env pwsh +$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent + +$exe="" +if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { + # Fix case when both the Windows and Linux builds of Node + # are installed in the same directory + $exe=".exe" +} +$ret=0 +if (Test-Path "$basedir/node$exe") { + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & "$basedir/node$exe" "$basedir/../node-gyp-build/optional.js" $args + } else { + & "$basedir/node$exe" "$basedir/../node-gyp-build/optional.js" $args + } + $ret=$LASTEXITCODE +} else { + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & "node$exe" "$basedir/../node-gyp-build/optional.js" $args + } else { + & "node$exe" "$basedir/../node-gyp-build/optional.js" $args + } + $ret=$LASTEXITCODE +} +exit $ret diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-test b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-test new file mode 100644 index 0000000..049fa6a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-test @@ -0,0 +1,12 @@ +#!/bin/sh +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") + +case `uname` in + *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;; +esac + +if [ -x "$basedir/node" ]; then + exec "$basedir/node" "$basedir/../node-gyp-build/build-test.js" "$@" +else + exec node "$basedir/../node-gyp-build/build-test.js" "$@" +fi diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-test.cmd b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-test.cmd new file mode 100644 index 0000000..182a757 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-test.cmd @@ -0,0 +1,17 @@ +@ECHO off +GOTO start +:find_dp0 +SET dp0=%~dp0 +EXIT /b +:start +SETLOCAL +CALL :find_dp0 + +IF EXIST "%dp0%\node.exe" ( + SET "_prog=%dp0%\node.exe" +) ELSE ( + SET "_prog=node" + SET PATHEXT=%PATHEXT:;.JS;=;% +) + +endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\node-gyp-build\build-test.js" %* diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-test.ps1 b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-test.ps1 new file mode 100644 index 0000000..6cb0b9b --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build-test.ps1 @@ -0,0 +1,28 @@ +#!/usr/bin/env pwsh +$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent + +$exe="" +if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { + # Fix case when both the Windows and Linux builds of Node + # are installed in the same directory + $exe=".exe" +} +$ret=0 +if (Test-Path "$basedir/node$exe") { + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & "$basedir/node$exe" "$basedir/../node-gyp-build/build-test.js" $args + } else { + & "$basedir/node$exe" "$basedir/../node-gyp-build/build-test.js" $args + } + $ret=$LASTEXITCODE +} else { + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & "node$exe" "$basedir/../node-gyp-build/build-test.js" $args + } else { + & "node$exe" "$basedir/../node-gyp-build/build-test.js" $args + } + $ret=$LASTEXITCODE +} +exit $ret diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build.cmd b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build.cmd new file mode 100644 index 0000000..ac854a6 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build.cmd @@ -0,0 +1,17 @@ +@ECHO off +GOTO start +:find_dp0 +SET dp0=%~dp0 +EXIT /b +:start +SETLOCAL +CALL :find_dp0 + +IF EXIST "%dp0%\node.exe" ( + SET "_prog=%dp0%\node.exe" +) ELSE ( + SET "_prog=node" + SET PATHEXT=%PATHEXT:;.JS;=;% +) + +endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\node-gyp-build\bin.js" %* diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build.ps1 b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build.ps1 new file mode 100644 index 0000000..c1f9a9a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.bin/node-gyp-build.ps1 @@ -0,0 +1,28 @@ +#!/usr/bin/env pwsh +$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent + +$exe="" +if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { + # Fix case when both the Windows and Linux builds of Node + # are installed in the same directory + $exe=".exe" +} +$ret=0 +if (Test-Path "$basedir/node$exe") { + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & "$basedir/node$exe" "$basedir/../node-gyp-build/bin.js" $args + } else { + & "$basedir/node$exe" "$basedir/../node-gyp-build/bin.js" $args + } + $ret=$LASTEXITCODE +} else { + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & "node$exe" "$basedir/../node-gyp-build/bin.js" $args + } else { + & "node$exe" "$basedir/../node-gyp-build/bin.js" $args + } + $ret=$LASTEXITCODE +} +exit $ret diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.package-lock.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.package-lock.json new file mode 100644 index 0000000..6903bd4 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/.package-lock.json @@ -0,0 +1,243 @@ +{ + "name": "anti-recall", + "lockfileVersion": 3, + "requires": true, + "packages": { + "node_modules/abstract-level": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.4.tgz", + "integrity": "sha512-eUP/6pbXBkMbXFdx4IH2fVgvB7M0JvR7/lIL33zcs0IBcwjdzSSl31TOJsaCzmKSSDF9h8QYSOJux4Nd4YJqFg==", + "dependencies": { + "buffer": "^6.0.3", + "catering": "^2.1.0", + "is-buffer": "^2.0.5", + "level-supports": "^4.0.0", + "level-transcoder": "^1.0.1", + "module-error": "^1.0.1", + "queue-microtask": "^1.2.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/browser-level": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz", + "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==", + "dependencies": { + "abstract-level": "^1.0.2", + "catering": "^2.1.1", + "module-error": "^1.0.2", + "run-parallel-limit": "^1.1.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/catering": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", + "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==", + "engines": { + "node": ">=6" + } + }, + "node_modules/classic-level": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.4.1.tgz", + "integrity": "sha512-qGx/KJl3bvtOHrGau2WklEZuXhS3zme+jf+fsu6Ej7W7IP/C49v7KNlWIsT1jZu0YnfzSIYDGcEWpCa1wKGWXQ==", + "hasInstallScript": true, + "dependencies": { + "abstract-level": "^1.0.2", + "catering": "^2.1.0", + "module-error": "^1.0.1", + "napi-macros": "^2.2.2", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/level": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/level/-/level-8.0.1.tgz", + "integrity": "sha512-oPBGkheysuw7DmzFQYyFe8NAia5jFLAgEnkgWnK3OXAuJr8qFT+xBQIwokAZPME2bhPFzS8hlYcL16m8UZrtwQ==", + "dependencies": { + "abstract-level": "^1.0.4", + "browser-level": "^1.0.1", + "classic-level": "^1.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/level" + } + }, + "node_modules/level-supports": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", + "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/level-transcoder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", + "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", + "dependencies": { + "buffer": "^6.0.3", + "module-error": "^1.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/module-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", + "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/napi-macros": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.2.2.tgz", + "integrity": "sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==" + }, + "node_modules/node-gyp-build": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", + "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/run-parallel-limit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz", + "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + } + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/CHANGELOG.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/CHANGELOG.md new file mode 100644 index 0000000..7efb8a5 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/CHANGELOG.md @@ -0,0 +1,48 @@ +# Changelog + +## [1.0.4] - 2024-01-20 + +### Fixed + +- Fix TypeScript definitions of `all()` and `nextv()` ([#67](https://github.com/Level/abstract-level/issues/67)) ([`8e85993`](https://github.com/Level/abstract-level/commit/8e85993), [`9f17757`](https://github.com/Level/abstract-level/commit/9f17757)) (Bryan) + +## [1.0.3] - 2022-03-20 + +### Added + +- Document error codes of `classic-level` and `many-level` ([#20](https://github.com/Level/abstract-level/issues/20)) ([`4b3464c`](https://github.com/Level/abstract-level/commit/4b3464c)) (Vincent Weevers) + +### Fixed + +- Add hidden `abortOnClose` option to iterators ([`2935180`](https://github.com/Level/abstract-level/commit/2935180)) (Vincent Weevers) +- Make internal iterator decoding options enumerable ([`eb08363`](https://github.com/Level/abstract-level/commit/eb08363)) (Vincent Weevers) +- Restore Sauce Labs browser tests ([`90b8816`](https://github.com/Level/abstract-level/commit/90b8816)) (Vincent Weevers) + +## [1.0.2] - 2022-03-06 + +### Fixed + +- Fix TypeScript declaration of chained batch `write()` options ([`392b7f7`](https://github.com/Level/abstract-level/commit/392b7f7)) (Vincent Weevers) +- Document the return type of `db.batch()` and add example ([`9739bba`](https://github.com/Level/abstract-level/commit/9739bba)) (Vincent Weevers) + +## [1.0.1] - 2022-02-06 + +### Fixed + +- Add `highWaterMarkBytes` option to tests where it matters ([`6b25a91`](https://github.com/Level/abstract-level/commit/6b25a91)) (Vincent Weevers) +- Clarify the meaning of `db.status` ([`2e90b05`](https://github.com/Level/abstract-level/commit/2e90b05)) (Vincent Weevers) +- Use `new` in README examples ([`379503e`](https://github.com/Level/abstract-level/commit/379503e)) (Vincent Weevers). + +## [1.0.0] - 2022-01-30 + +_:seedling: Initial release. If you are upgrading from `abstract-leveldown` please see [`UPGRADING.md`](UPGRADING.md)_ + +[1.0.4]: https://github.com/Level/abstract-level/releases/tag/v1.0.4 + +[1.0.3]: https://github.com/Level/abstract-level/releases/tag/v1.0.3 + +[1.0.2]: https://github.com/Level/abstract-level/releases/tag/v1.0.2 + +[1.0.1]: https://github.com/Level/abstract-level/releases/tag/v1.0.1 + +[1.0.0]: https://github.com/Level/abstract-level/releases/tag/v1.0.0 diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/LICENSE new file mode 100644 index 0000000..2b3ffdb --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2013 Rod Vagg and the contributors to abstract-level. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/README.md new file mode 100644 index 0000000..d772630 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/README.md @@ -0,0 +1,1436 @@ +# abstract-level + +**Abstract class for a lexicographically sorted key-value database.** The successor to [`abstract-leveldown`](https://github.com/Level/abstract-leveldown) with builtin encodings, sublevels, events, promises and support of Uint8Array. If you are upgrading please see [`UPGRADING.md`](UPGRADING.md). + +> :pushpin: Which module should I use? What happened to `levelup`? Head over to the [FAQ](https://github.com/Level/community#faq). + +[![level badge][level-badge]](https://github.com/Level/awesome) +[![npm](https://img.shields.io/npm/v/abstract-level.svg)](https://www.npmjs.com/package/abstract-level) +[![Node version](https://img.shields.io/node/v/abstract-level.svg)](https://www.npmjs.com/package/abstract-level) +[![Test](https://img.shields.io/github/workflow/status/Level/abstract-level/Test?label=test)](https://github.com/Level/abstract-level/actions/workflows/test.yml) +[![Browsers](https://img.shields.io/github/workflow/status/Level/abstract-level/Browsers?label=browsers)](https://github.com/Level/abstract-level/actions/workflows/browsers.yml) +[![Coverage](https://img.shields.io/codecov/c/github/Level/abstract-level?label=\&logo=codecov\&logoColor=fff)](https://codecov.io/gh/Level/abstract-level) +[![Standard](https://img.shields.io/badge/standard-informational?logo=javascript\&logoColor=fff)](https://standardjs.com) +[![Common Changelog](https://common-changelog.org/badge.svg)](https://common-changelog.org) +[![Donate](https://img.shields.io/badge/donate-orange?logo=open-collective\&logoColor=fff)](https://opencollective.com/level) + +## Table of Contents + +
Click to expand + +- [Usage](#usage) +- [Supported Platforms](#supported-platforms) +- [Public API For Consumers](#public-api-for-consumers) + - [`db = new Constructor(...[, options])`](#db--new-constructor-options) + - [`db.status`](#dbstatus) + - [`db.open([options][, callback])`](#dbopenoptions-callback) + - [`db.close([callback])`](#dbclosecallback) + - [`db.supports`](#dbsupports) + - [`db.get(key[, options][, callback])`](#dbgetkey-options-callback) + - [`db.getMany(keys[, options][, callback])`](#dbgetmanykeys-options-callback) + - [`db.put(key, value[, options][, callback])`](#dbputkey-value-options-callback) + - [`db.del(key[, options][, callback])`](#dbdelkey-options-callback) + - [`db.batch(operations[, options][, callback])`](#dbbatchoperations-options-callback) + - [`chainedBatch = db.batch()`](#chainedbatch--dbbatch) + - [`iterator = db.iterator([options])`](#iterator--dbiteratoroptions) + - [`keyIterator = db.keys([options])`](#keyiterator--dbkeysoptions) + - [`valueIterator = db.values([options])`](#valueiterator--dbvaluesoptions) + - [`db.clear([options][, callback])`](#dbclearoptions-callback) + - [`sublevel = db.sublevel(name[, options])`](#sublevel--dbsublevelname-options) + - [`encoding = db.keyEncoding([encoding])`](#encoding--dbkeyencodingencoding) + - [`encoding = db.valueEncoding([encoding])`](#encoding--dbvalueencodingencoding) + - [`key = db.prefixKey(key, keyFormat)`](#key--dbprefixkeykey-keyformat) + - [`db.defer(fn)`](#dbdeferfn) + - [`chainedBatch`](#chainedbatch) + - [`chainedBatch.put(key, value[, options])`](#chainedbatchputkey-value-options) + - [`chainedBatch.del(key[, options])`](#chainedbatchdelkey-options) + - [`chainedBatch.clear()`](#chainedbatchclear) + - [`chainedBatch.write([options][, callback])`](#chainedbatchwriteoptions-callback) + - [`chainedBatch.close([callback])`](#chainedbatchclosecallback) + - [`chainedBatch.length`](#chainedbatchlength) + - [`chainedBatch.db`](#chainedbatchdb) + - [`iterator`](#iterator) + - [`for await...of iterator`](#for-awaitof-iterator) + - [`iterator.next([callback])`](#iteratornextcallback) + - [`iterator.nextv(size[, options][, callback])`](#iteratornextvsize-options-callback) + - [`iterator.all([options][, callback])`](#iteratoralloptions-callback) + - [`iterator.seek(target[, options])`](#iteratorseektarget-options) + - [`iterator.close([callback])`](#iteratorclosecallback) + - [`iterator.db`](#iteratordb) + - [`iterator.count`](#iteratorcount) + - [`iterator.limit`](#iteratorlimit) + - [`keyIterator`](#keyiterator) + - [`valueIterator`](#valueiterator) + - [`sublevel`](#sublevel) + - [`sublevel.prefix`](#sublevelprefix) + - [`sublevel.db`](#subleveldb) + - [Encodings](#encodings) + - [Events](#events) + - [Errors](#errors) + - [`LEVEL_NOT_FOUND`](#level_not_found) + - [`LEVEL_DATABASE_NOT_OPEN`](#level_database_not_open) + - [`LEVEL_DATABASE_NOT_CLOSED`](#level_database_not_closed) + - [`LEVEL_ITERATOR_NOT_OPEN`](#level_iterator_not_open) + - [`LEVEL_ITERATOR_BUSY`](#level_iterator_busy) + - [`LEVEL_BATCH_NOT_OPEN`](#level_batch_not_open) + - [`LEVEL_ENCODING_NOT_FOUND`](#level_encoding_not_found) + - [`LEVEL_ENCODING_NOT_SUPPORTED`](#level_encoding_not_supported) + - [`LEVEL_DECODE_ERROR`](#level_decode_error) + - [`LEVEL_INVALID_KEY`](#level_invalid_key) + - [`LEVEL_INVALID_VALUE`](#level_invalid_value) + - [`LEVEL_CORRUPTION`](#level_corruption) + - [`LEVEL_IO_ERROR`](#level_io_error) + - [`LEVEL_INVALID_PREFIX`](#level_invalid_prefix) + - [`LEVEL_NOT_SUPPORTED`](#level_not_supported) + - [`LEVEL_LEGACY`](#level_legacy) + - [`LEVEL_LOCKED`](#level_locked) + - [`LEVEL_READONLY`](#level_readonly) + - [`LEVEL_CONNECTION_LOST`](#level_connection_lost) + - [`LEVEL_REMOTE_ERROR`](#level_remote_error) + - [Shared Access](#shared-access) +- [Private API For Implementors](#private-api-for-implementors) + - [Example](#example) + - [`db = AbstractLevel(manifest[, options])`](#db--abstractlevelmanifest-options) + - [`db._open(options, callback)`](#db_openoptions-callback) + - [`db._close(callback)`](#db_closecallback) + - [`db._get(key, options, callback)`](#db_getkey-options-callback) + - [`db._getMany(keys, options, callback)`](#db_getmanykeys-options-callback) + - [`db._put(key, value, options, callback)`](#db_putkey-value-options-callback) + - [`db._del(key, options, callback)`](#db_delkey-options-callback) + - [`db._batch(operations, options, callback)`](#db_batchoperations-options-callback) + - [`db._chainedBatch()`](#db_chainedbatch) + - [`db._iterator(options)`](#db_iteratoroptions) + - [`db._keys(options)`](#db_keysoptions) + - [`db._values(options)`](#db_valuesoptions) + - [`db._clear(options, callback)`](#db_clearoptions-callback) + - [`sublevel = db._sublevel(name, options)`](#sublevel--db_sublevelname-options) + - [`iterator = AbstractIterator(db, options)`](#iterator--abstractiteratordb-options) + - [`iterator._next(callback)`](#iterator_nextcallback) + - [`iterator._nextv(size, options, callback)`](#iterator_nextvsize-options-callback) + - [`iterator._all(options, callback)`](#iterator_alloptions-callback) + - [`iterator._seek(target, options)`](#iterator_seektarget-options) + - [`iterator._close(callback)`](#iterator_closecallback) + - [`keyIterator = AbstractKeyIterator(db, options)`](#keyiterator--abstractkeyiteratordb-options) + - [`valueIterator = AbstractValueIterator(db, options)`](#valueiterator--abstractvalueiteratordb-options) + - [`chainedBatch = AbstractChainedBatch(db)`](#chainedbatch--abstractchainedbatchdb) + - [`chainedBatch._put(key, value, options)`](#chainedbatch_putkey-value-options) + - [`chainedBatch._del(key, options)`](#chainedbatch_delkey-options) + - [`chainedBatch._clear()`](#chainedbatch_clear) + - [`chainedBatch._write(options, callback)`](#chainedbatch_writeoptions-callback) + - [`chainedBatch._close(callback)`](#chainedbatch_closecallback) +- [Test Suite](#test-suite) + - [Excluding tests](#excluding-tests) + - [Reusing `testCommon`](#reusing-testcommon) +- [Spread The Word](#spread-the-word) +- [Install](#install) +- [Contributing](#contributing) +- [Donate](#donate) +- [License](#license) + +
+ +## Usage + +Usage of a typical implementation looks as follows. + +```js +// Create a database +const db = new Level('./db', { valueEncoding: 'json' }) + +// Add an entry with key 'a' and value 1 +await db.put('a', 1) + +// Add multiple entries +await db.batch([{ type: 'put', key: 'b', value: 2 }]) + +// Get value of key 'a': 1 +const value = await db.get('a') + +// Iterate entries with keys that are greater than 'a' +for await (const [key, value] of db.iterator({ gt: 'a' })) { + console.log(value) // 2 +} +``` + +All asynchronous methods also support callbacks. + +
Callback example + +```js +db.put('a', { x: 123 }, function (err) { + if (err) throw err + + db.get('a', function (err, value) { + console.log(value) // { x: 123 } + }) +}) +``` + +
+ +Usage from TypeScript requires generic type parameters. + +
TypeScript example + +```ts +// Specify types of keys and values (any, in the case of json). +// The generic type parameters default to Level. +const db = new Level('./db', { valueEncoding: 'json' }) + +// All relevant methods then use those types +await db.put('a', { x: 123 }) + +// Specify different types when overriding encoding per operation +await db.get('a', { valueEncoding: 'utf8' }) + +// Though in some cases TypeScript can infer them +await db.get('a', { valueEncoding: db.valueEncoding('utf8') }) + +// It works the same for sublevels +const abc = db.sublevel('abc') +const xyz = db.sublevel('xyz', { valueEncoding: 'json' }) +``` + +
+ +## Supported Platforms + +We aim to support Active LTS and Current Node.js releases, as well as evergreen browsers that are based on Chromium, Firefox or Webkit. Supported runtime environments may differ per implementation. + +## Public API For Consumers + +This module has a public API for consumers of a database and a [private API](#private-api-for-implementors) for concrete implementations. The public API, as documented in this section, offers a simple yet rich interface that is common between all implementations. Implementations may have additional options or methods. TypeScript [type declarations](https://www.typescriptlang.org/docs/handbook/2/type-declarations.html) are [included](./index.d.ts) (and exported for reuse) only for the public API. + +An `abstract-level` database is at its core a [key-value database](https://en.wikipedia.org/wiki/Key%E2%80%93value_database). A key-value pair is referred to as an _entry_ here and typically returned as an array, comparable to [`Object.entries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries). + +### `db = new Constructor(...[, options])` + +Creating a database is done by calling a class constructor. Implementations export a class that extends the [`AbstractLevel`](./abstract-level.js) class and has its own constructor with an implementation-specific signature. All constructors should have an `options` argument as the last. Typically, constructors take a `location` as their first argument, pointing to where the data will be stored. That may be a file path, URL, something else or none at all, since not all implementations are disk-based or persistent. Others take another database rather than a location as their first argument. + +The optional `options` object may contain: + +- `keyEncoding` (string or object, default `'utf8'`): encoding to use for keys +- `valueEncoding` (string or object, default `'utf8'`): encoding to use for values. + +See [Encodings](#encodings) for a full description of these options. Other `options` (except `passive`) are forwarded to `db.open()` which is automatically called in a next tick after the constructor returns. Any read & write operations are queued internally until the database has finished opening. If opening fails, those queued operations will yield errors. + +### `db.status` + +Read-only getter that returns a string reflecting the current state of the database: + +- `'opening'` - waiting for the database to be opened +- `'open'` - successfully opened the database +- `'closing'` - waiting for the database to be closed +- `'closed'` - successfully closed the database. + +### `db.open([options][, callback])` + +Open the database. The `callback` function will be called with no arguments when successfully opened, or with a single error argument if opening failed. If no callback is provided, a promise is returned. Options passed to `open()` take precedence over options passed to the database constructor. Not all implementations support the `createIfMissing` and `errorIfExists` options (notably [`memory-level`](https://github.com/Level/memory-level) and [`browser-level`](https://github.com/Level/browser-level)) and will indicate so via `db.supports.createIfMissing` and `db.supports.errorIfExists`. + +The optional `options` object may contain: + +- `createIfMissing` (boolean, default: `true`): If `true`, create an empty database if one doesn't already exist. If `false` and the database doesn't exist, opening will fail. +- `errorIfExists` (boolean, default: `false`): If `true` and the database already exists, opening will fail. +- `passive` (boolean, default: `false`): Wait for, but do not initiate, opening of the database. + +It's generally not necessary to call `open()` because it's automatically called by the database constructor. It may however be useful to capture an error from failure to open, that would otherwise not surface until another method like `db.get()` is called. It's also possible to reopen the database after it has been closed with [`close()`](#dbclosecallback). Once `open()` has then been called, any read & write operations will again be queued internally until opening has finished. + +The `open()` and `close()` methods are idempotent. If the database is already open, the `callback` will be called in a next tick. If opening is already in progress, the `callback` will be called when that has finished. If closing is in progress, the database will be reopened once closing has finished. Likewise, if `close()` is called after `open()`, the database will be closed once opening has finished and the prior `open()` call will receive an error. + +### `db.close([callback])` + +Close the database. The `callback` function will be called with no arguments if closing succeeded or with a single `error` argument if closing failed. If no callback is provided, a promise is returned. + +A database may have associated resources like file handles and locks. When the database is no longer needed (for the remainder of a program) it's recommended to call `db.close()` to free up resources. + +After `db.close()` has been called, no further read & write operations are allowed unless and until `db.open()` is called again. For example, `db.get(key)` will yield an error with code [`LEVEL_DATABASE_NOT_OPEN`](#errors). Any unclosed iterators or chained batches will be closed by `db.close()` and can then no longer be used even when `db.open()` is called again. + +### `db.supports` + +A [manifest](https://github.com/Level/supports) describing the features supported by this database. Might be used like so: + +```js +if (!db.supports.permanence) { + throw new Error('Persistent storage is required') +} +``` + +### `db.get(key[, options][, callback])` + +Get a value from the database by `key`. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. +- `valueEncoding`: custom value encoding for this operation, used to decode the value. + +The `callback` function will be called with an error if the operation failed. If the key was not found, the error will have code [`LEVEL_NOT_FOUND`](#errors). If successful the first argument will be `null` and the second argument will be the value. If no callback is provided, a promise is returned. + +If the database indicates support of snapshots via `db.supports.snapshots` then `db.get()` _should_ read from a snapshot of the database, created at the time `db.get()` was called. This means it should not see the data of simultaneous write operations. However, this is currently not verified by the [abstract test suite](#test-suite). + +### `db.getMany(keys[, options][, callback])` + +Get multiple values from the database by an array of `keys`. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `keys`. +- `valueEncoding`: custom value encoding for this operation, used to decode values. + +The `callback` function will be called with an error if the operation failed. If successful the first argument will be `null` and the second argument will be an array of values with the same order as `keys`. If a key was not found, the relevant value will be `undefined`. If no callback is provided, a promise is returned. + +If the database indicates support of snapshots via `db.supports.snapshots` then `db.getMany()` _should_ read from a snapshot of the database, created at the time `db.getMany()` was called. This means it should not see the data of simultaneous write operations. However, this is currently not verified by the [abstract test suite](#test-suite). + +### `db.put(key, value[, options][, callback])` + +Add a new entry or overwrite an existing entry. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. +- `valueEncoding`: custom value encoding for this operation, used to encode the `value`. + +The `callback` function will be called with no arguments if the operation was successful or with an error if it failed. If no callback is provided, a promise is returned. + +### `db.del(key[, options][, callback])` + +Delete an entry by `key`. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. + +The `callback` function will be called with no arguments if the operation was successful or with an error if it failed. If no callback is provided, a promise is returned. + +### `db.batch(operations[, options][, callback])` + +Perform multiple _put_ and/or _del_ operations in bulk. The `operations` argument must be an array containing a list of operations to be executed sequentially, although as a whole they are performed as an atomic operation. + +Each operation must be an object with at least a `type` property set to either `'put'` or `'del'`. If the `type` is `'put'`, the operation must have `key` and `value` properties. It may optionally have `keyEncoding` and / or `valueEncoding` properties to encode keys or values with a custom encoding for just that operation. If the `type` is `'del'`, the operation must have a `key` property and may optionally have a `keyEncoding` property. + +An operation of either type may also have a `sublevel` property, to prefix the key of the operation with the prefix of that sublevel. This allows atomically committing data to multiple sublevels. Keys and values will be encoded by the sublevel, to the same effect as a `sublevel.batch(..)` call. In the following example, the first `value` will be encoded with `'json'` rather than the default encoding of `db`: + +```js +const people = db.sublevel('people', { valueEncoding: 'json' }) +const nameIndex = db.sublevel('names') + +await db.batch([{ + type: 'put', + sublevel: people, + key: '123', + value: { + name: 'Alice' + } +}, { + type: 'put', + sublevel: nameIndex, + key: 'Alice', + value: '123' +}]) +``` + +The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this batch, used to encode keys. +- `valueEncoding`: custom value encoding for this batch, used to encode values. + +Encoding properties on individual operations take precedence. In the following example, the first value will be encoded with the `'utf8'` encoding and the second with `'json'`. + +```js +await db.batch([ + { type: 'put', key: 'a', value: 'foo' }, + { type: 'put', key: 'b', value: 123, valueEncoding: 'json' } +], { valueEncoding: 'utf8' }) +``` + +The `callback` function will be called with no arguments if the batch was successful or with an error if it failed. If no callback is provided, a promise is returned. + +### `chainedBatch = db.batch()` + +Create a [chained batch](#chainedbatch), when `batch()` is called with zero arguments. A chained batch can be used to build and eventually commit an atomic batch of operations. Depending on how it's used, it is possible to obtain greater performance with this form of `batch()`. On several implementations however, it is just sugar. + +```js +await db.batch() + .del('bob') + .put('alice', 361) + .put('kim', 220) + .write() +``` + +### `iterator = db.iterator([options])` + +Create an [iterator](#iterator). The optional `options` object may contain the following _range options_ to control the range of entries to be iterated: + +- `gt` (greater than) or `gte` (greater than or equal): define the lower bound of the range to be iterated. Only entries where the key is greater than (or equal to) this option will be included in the range. When `reverse` is true the order will be reversed, but the entries iterated will be the same. +- `lt` (less than) or `lte` (less than or equal): define the higher bound of the range to be iterated. Only entries where the key is less than (or equal to) this option will be included in the range. When `reverse` is true the order will be reversed, but the entries iterated will be the same. +- `reverse` (boolean, default: `false`): iterate entries in reverse order. Beware that a reverse seek can be slower than a forward seek. +- `limit` (number, default: `Infinity`): limit the number of entries yielded. This number represents a _maximum_ number of entries and will not be reached if the end of the range is reached first. A value of `Infinity` or `-1` means there is no limit. When `reverse` is true the entries with the highest keys will be returned instead of the lowest keys. + +The `gte` and `lte` range options take precedence over `gt` and `lt` respectively. If no range options are provided, the iterator will visit all entries of the database, starting at the lowest key and ending at the highest key (unless `reverse` is true). In addition to range options, the `options` object may contain: + +- `keys` (boolean, default: `true`): whether to return the key of each entry. If set to `false`, the iterator will yield keys that are `undefined`. Prefer to use `db.keys()` instead. +- `values` (boolean, default: `true`): whether to return the value of each entry. If set to `false`, the iterator will yield values that are `undefined`. Prefer to use `db.values()` instead. +- `keyEncoding`: custom key encoding for this iterator, used to encode range options, to encode `seek()` targets and to decode keys. +- `valueEncoding`: custom value encoding for this iterator, used to decode values. + +Lastly, an implementation is free to add its own options. + +> :pushpin: To instead consume data using streams, see [`level-read-stream`](https://github.com/Level/read-stream) and [`level-web-stream`](https://github.com/Level/web-stream). + +### `keyIterator = db.keys([options])` + +Create a [key iterator](#keyiterator), having the same interface as `db.iterator()` except that it yields keys instead of entries. If only keys are needed, using `db.keys()` may increase performance because values won't have to fetched, copied or decoded. Options are the same as for `db.iterator()` except that `db.keys()` does not take `keys`, `values` and `valueEncoding` options. + +```js +// Iterate lazily +for await (const key of db.keys({ gt: 'a' })) { + console.log(key) +} + +// Get all at once. Setting a limit is recommended. +const keys = await db.keys({ gt: 'a', limit: 10 }).all() +``` + +### `valueIterator = db.values([options])` + +Create a [value iterator](#valueiterator), having the same interface as `db.iterator()` except that it yields values instead of entries. If only values are needed, using `db.values()` may increase performance because keys won't have to fetched, copied or decoded. Options are the same as for `db.iterator()` except that `db.values()` does not take `keys` and `values` options. Note that it _does_ take a `keyEncoding` option, relevant for the encoding of range options. + +```js +// Iterate lazily +for await (const value of db.values({ gt: 'a' })) { + console.log(value) +} + +// Get all at once. Setting a limit is recommended. +const values = await db.values({ gt: 'a', limit: 10 }).all() +``` + +### `db.clear([options][, callback])` + +Delete all entries or a range. Not guaranteed to be atomic. Accepts the following options (with the same rules as on iterators): + +- `gt` (greater than) or `gte` (greater than or equal): define the lower bound of the range to be deleted. Only entries where the key is greater than (or equal to) this option will be included in the range. When `reverse` is true the order will be reversed, but the entries deleted will be the same. +- `lt` (less than) or `lte` (less than or equal): define the higher bound of the range to be deleted. Only entries where the key is less than (or equal to) this option will be included in the range. When `reverse` is true the order will be reversed, but the entries deleted will be the same. +- `reverse` (boolean, default: `false`): delete entries in reverse order. Only effective in combination with `limit`, to delete the last N entries. +- `limit` (number, default: `Infinity`): limit the number of entries to be deleted. This number represents a _maximum_ number of entries and will not be reached if the end of the range is reached first. A value of `Infinity` or `-1` means there is no limit. When `reverse` is true the entries with the highest keys will be deleted instead of the lowest keys. +- `keyEncoding`: custom key encoding for this operation, used to encode range options. + +The `gte` and `lte` range options take precedence over `gt` and `lt` respectively. If no options are provided, all entries will be deleted. The `callback` function will be called with no arguments if the operation was successful or with an error if it failed. If no callback is provided, a promise is returned. + +### `sublevel = db.sublevel(name[, options])` + +Create a [sublevel](#sublevel) that has the same interface as `db` (except for additional, implementation-specific methods) and prefixes the keys of operations before passing them on to `db`. The `name` argument is required and must be a string. + +```js +const example = db.sublevel('example') + +await example.put('hello', 'world') +await db.put('a', '1') + +// Prints ['hello', 'world'] +for await (const [key, value] of example.iterator()) { + console.log([key, value]) +} +``` + +Sublevels effectively separate a database into sections. Think SQL tables, but evented, ranged and realtime! Each sublevel is an `AbstractLevel` instance with its own keyspace, [events](https://github.com/Level/abstract-level#events) and [encodings](https://github.com/Level/abstract-level#encodings). For example, it's possible to have one sublevel with `'buffer'` keys and another with `'utf8'` keys. The same goes for values. Like so: + +```js +db.sublevel('one', { valueEncoding: 'json' }) +db.sublevel('two', { keyEncoding: 'buffer' }) +``` + +An own keyspace means that `sublevel.iterator()` only includes entries of that sublevel, `sublevel.clear()` will only delete entries of that sublevel, and so forth. Range options get prefixed too. + +Fully qualified keys (as seen from the parent database) take the form of `prefix + key` where `prefix` is `separator + name + separator`. If `name` is empty, the effective prefix is two separators. Sublevels can be nested: if `db` is itself a sublevel then the effective prefix is a combined prefix, e.g. `'!one!!two!'`. Note that a parent database will see its own keys as well as keys of any nested sublevels: + +```js +// Prints ['!example!hello', 'world'] and ['a', '1'] +for await (const [key, value] of db.iterator()) { + console.log([key, value]) +} +``` + +> :pushpin: The key structure is equal to that of [`subleveldown`](https://github.com/Level/subleveldown) which offered sublevels before they were built-in to `abstract-level`. This means that an `abstract-level` sublevel can read sublevels previously created with (and populated by) `subleveldown`. + +Internally, sublevels operate on keys that are either a string, Buffer or Uint8Array, depending on parent database and choice of encoding. Which is to say: binary keys are fully supported. The `name` must however always be a string and can only contain ASCII characters. + +The optional `options` object may contain: + +- `separator` (string, default: `'!'`): Character for separating sublevel names from user keys and each other. Must sort before characters used in `name`. An error will be thrown if that's not the case. +- `keyEncoding` (string or object, default `'utf8'`): encoding to use for keys +- `valueEncoding` (string or object, default `'utf8'`): encoding to use for values. + +The `keyEncoding` and `valueEncoding` options are forwarded to the `AbstractLevel` constructor and work the same, as if a new, separate database was created. They default to `'utf8'` regardless of the encodings configured on `db`. Other options are forwarded too but `abstract-level` has no relevant options at the time of writing. For example, setting the `createIfMissing` option will have no effect. Why is that? + +Like regular databases, sublevels open themselves but they do not affect the state of the parent database. This means a sublevel can be individually closed and (re)opened. If the sublevel is created while the parent database is opening, it will wait for that to finish. If the parent database is closed, then opening the sublevel will fail and subsequent operations on the sublevel will yield errors with code [`LEVEL_DATABASE_NOT_OPEN`](#errors). + +### `encoding = db.keyEncoding([encoding])` + +Returns the given `encoding` argument as a normalized encoding object that follows the [`level-transcoder`](https://github.com/Level/transcoder) encoding interface. See [Encodings](#encodings) for an introduction. The `encoding` argument may be: + +- A string to select a known encoding by its name +- An object that follows one of the following interfaces: [`level-transcoder`](https://github.com/Level/transcoder#encoding-interface), [`level-codec`](https://github.com/Level/codec#encoding-format), [`abstract-encoding`](https://github.com/mafintosh/abstract-encoding), [`multiformats`](https://github.com/multiformats/js-multiformats/blob/master/src/codecs/interface.ts) +- A previously normalized encoding, such that `keyEncoding(x)` equals `keyEncoding(keyEncoding(x))` +- Omitted, `null` or `undefined`, in which case the default `keyEncoding` of the database is returned. + +Other methods that take `keyEncoding` or `valueEncoding` options, accept the same as above. Results are cached. If the `encoding` argument is an object and it has a name then subsequent calls can refer to that encoding by name. + +Depending on the encodings supported by a database, this method may return a _transcoder encoding_ that translates the desired encoding from / to an encoding supported by the database. Its `encode()` and `decode()` methods will have respectively the same input and output types as a non-transcoded encoding, but its `name` property will differ. + +Assume that e.g. `db.keyEncoding().encode(key)` is safe to call at any time including if the database isn't open, because encodings must be stateless. If the given encoding is not found or supported, a [`LEVEL_ENCODING_NOT_FOUND` or `LEVEL_ENCODING_NOT_SUPPORTED` error](#errors) is thrown. + +### `encoding = db.valueEncoding([encoding])` + +Same as `db.keyEncoding([encoding])` except that it returns the default `valueEncoding` of the database (if the `encoding` argument is omitted, `null` or `undefined`). + +### `key = db.prefixKey(key, keyFormat)` + +Add sublevel prefix to the given `key`, which must be already-encoded. If this database is not a sublevel, the given `key` is returned as-is. The `keyFormat` must be one of `'utf8'`, `'buffer'`, `'view'`. If `'utf8'` then `key` must be a string and the return value will be a string. If `'buffer'` then Buffer, if `'view'` then Uint8Array. + +```js +const sublevel = db.sublevel('example') + +console.log(db.prefixKey('a', 'utf8')) // 'a' +console.log(sublevel.prefixKey('a', 'utf8')) // '!example!a' +``` + +### `db.defer(fn)` + +Call the function `fn` at a later time when [`db.status`](#dbstatus) changes to `'open'` or `'closed'`. Used by `abstract-level` itself to implement "deferred open" which is a feature that makes it possible to call operations like `db.put()` before the database has finished opening. The `defer()` method is exposed for implementations and plugins to achieve the same on their custom operations: + +```js +db.foo = function (key, callback) { + if (this.status === 'opening') { + this.defer(() => this.foo(key, callback)) + } else { + // .. + } +} +``` + +When deferring a custom operation, do it early: after normalizing optional arguments but before encoding (to avoid double encoding and to emit original input if the operation has events) and before any _fast paths_ (to avoid calling back before the database has finished opening). For example, `db.batch([])` has an internal fast path where it skips work if the array of operations is empty. Resources that can be closed on their own (like iterators) should however first check such state before deferring, in order to reject operations after close (including when the database was reopened). + +### `chainedBatch` + +#### `chainedBatch.put(key, value[, options])` + +Queue a `put` operation on this batch, not committed until `write()` is called. This will throw a [`LEVEL_INVALID_KEY`](#errors) or [`LEVEL_INVALID_VALUE`](#errors) error if `key` or `value` is invalid. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. +- `valueEncoding`: custom value encoding for this operation, used to encode the `value`. +- `sublevel` (sublevel instance): act as though the `put` operation is performed on the given sublevel, to similar effect as `sublevel.batch().put(key, value)`. This allows atomically committing data to multiple sublevels. The `key` will be prefixed with the `prefix` of the sublevel, and the `key` and `value` will be encoded by the sublevel (using the default encodings of the sublevel unless `keyEncoding` and / or `valueEncoding` are provided). + +#### `chainedBatch.del(key[, options])` + +Queue a `del` operation on this batch, not committed until `write()` is called. This will throw a [`LEVEL_INVALID_KEY`](#errors) error if `key` is invalid. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. +- `sublevel` (sublevel instance): act as though the `del` operation is performed on the given sublevel, to similar effect as `sublevel.batch().del(key)`. This allows atomically committing data to multiple sublevels. The `key` will be prefixed with the `prefix` of the sublevel, and the `key` will be encoded by the sublevel (using the default key encoding of the sublevel unless `keyEncoding` is provided). + +#### `chainedBatch.clear()` + +Clear all queued operations on this batch. + +#### `chainedBatch.write([options][, callback])` + +Commit the queued operations for this batch. All operations will be written atomically, that is, they will either all succeed or fail with no partial commits. + +There are no `options` by default but implementations may add theirs. Note that `write()` does not take encoding options. Those can only be set on `put()` and `del()` because implementations may synchronously forward such calls to an underlying store and thus need keys and values to be encoded at that point. + +The `callback` function will be called with no arguments if the batch was successful or with an error if it failed. If no callback is provided, a promise is returned. + +After `write()` or `close()` has been called, no further operations are allowed. + +#### `chainedBatch.close([callback])` + +Free up underlying resources. This should be done even if the chained batch has zero queued operations. Automatically called by `write()` so normally not necessary to call, unless the intent is to discard a chained batch without committing it. The `callback` function will be called with no arguments. If no callback is provided, a promise is returned. Closing the batch is an idempotent operation, such that calling `close()` more than once is allowed and makes no difference. + +#### `chainedBatch.length` + +The number of queued operations on the current batch. + +#### `chainedBatch.db` + +A reference to the database that created this chained batch. + +### `iterator` + +An iterator allows one to lazily read a range of entries stored in the database. The entries will be sorted by keys in [lexicographic order](https://en.wikipedia.org/wiki/Lexicographic_order) (in other words: byte order) which in short means key `'a'` comes before `'b'` and key `'10'` comes before `'2'`. + +An iterator reads from a snapshot of the database, created at the time `db.iterator()` was called. This means the iterator will not see the data of simultaneous write operations. Most but not all implementations can offer this guarantee, as indicated by `db.supports.snapshots`. + +Iterators can be consumed with [`for await...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) and `iterator.all()`, or by manually calling `iterator.next()` or `nextv()` in succession. In the latter case, `iterator.close()` must always be called. In contrast, finishing, throwing, breaking or returning from a `for await...of` loop automatically calls `iterator.close()`, as does `iterator.all()`. + +An iterator reaches its natural end in the following situations: + +- The end of the database has been reached +- The end of the range has been reached +- The last `iterator.seek()` was out of range. + +An iterator keeps track of calls that are in progress. It doesn't allow concurrent `next()`, `nextv()` or `all()` calls (including a combination thereof) and will throw an error with code [`LEVEL_ITERATOR_BUSY`](#errors) if that happens: + +```js +// Not awaited and no callback provided +iterator.next() + +try { + // Which means next() is still in progress here + iterator.all() +} catch (err) { + console.log(err.code) // 'LEVEL_ITERATOR_BUSY' +} +``` + +#### `for await...of iterator` + +Yields entries, which are arrays containing a `key` and `value`. The type of `key` and `value` depends on the options passed to `db.iterator()`. + +```js +try { + for await (const [key, value] of db.iterator()) { + console.log(key) + } +} catch (err) { + console.error(err) +} +``` + +Note for implementors: this uses `iterator.next()` and `iterator.close()` under the hood so no further method implementations are needed to support `for await...of`. + +#### `iterator.next([callback])` + +Advance to the next entry and yield that entry. If an error occurs, the `callback` function will be called with an error. Otherwise, the `callback` receives `null`, a `key` and a `value`. The type of `key` and `value` depends on the options passed to `db.iterator()`. If the iterator has reached its natural end, both `key` and `value` will be `undefined`. + +If no callback is provided, a promise is returned for either an entry array (containing a `key` and `value`) or `undefined` if the iterator reached its natural end. + +**Note:** `iterator.close()` must always be called once there's no intention to call `next()` or `nextv()` again. Even if such calls yielded an error and even if the iterator reached its natural end. Not closing the iterator will result in memory leaks and may also affect performance of other operations if many iterators are unclosed and each is holding a snapshot of the database. + +#### `iterator.nextv(size[, options][, callback])` + +Advance repeatedly and get at most `size` amount of entries in a single call. Can be faster than repeated `next()` calls. The `size` argument must be an integer and has a soft minimum of 1. There are no `options` by default but implementations may add theirs. + +If an error occurs, the `callback` function will be called with an error. Otherwise, the `callback` receives `null` and an array of entries, where each entry is an array containing a key and value. The natural end of the iterator will be signaled by yielding an empty array. If no callback is provided, a promise is returned. + +```js +const iterator = db.iterator() + +while (true) { + const entries = await iterator.nextv(100) + + if (entries.length === 0) { + break + } + + for (const [key, value] of entries) { + // .. + } +} + +await iterator.close() +``` + +#### `iterator.all([options][, callback])` + +Advance repeatedly and get all (remaining) entries as an array, automatically closing the iterator. Assumes that those entries fit in memory. If that's not the case, instead use `next()`, `nextv()` or `for await...of`. There are no `options` by default but implementations may add theirs. If an error occurs, the `callback` function will be called with an error. Otherwise, the `callback` receives `null` and an array of entries, where each entry is an array containing a key and value. If no callback is provided, a promise is returned. + +```js +const entries = await db.iterator({ limit: 100 }).all() + +for (const [key, value] of entries) { + // .. +} +``` + +#### `iterator.seek(target[, options])` + +Seek to the key closest to `target`. Subsequent calls to `iterator.next()`, `nextv()` or `all()` (including implicit calls in a `for await...of` loop) will yield entries with keys equal to or larger than `target`, or equal to or smaller than `target` if the `reverse` option passed to `db.iterator()` was true. + +The optional `options` object may contain: + +- `keyEncoding`: custom key encoding, used to encode the `target`. By default the `keyEncoding` option of the iterator is used or (if that wasn't set) the `keyEncoding` of the database. + +If range options like `gt` were passed to `db.iterator()` and `target` does not fall within that range, the iterator will reach its natural end. + +**Note:** Not all implementations support `seek()`. Consult `db.supports.seek` or the [support matrix](https://github.com/Level/supports#seek-boolean). + +#### `iterator.close([callback])` + +Free up underlying resources. The `callback` function will be called with no arguments. If no callback is provided, a promise is returned. Closing the iterator is an idempotent operation, such that calling `close()` more than once is allowed and makes no difference. + +If a `next()` ,`nextv()` or `all()` call is in progress, closing will wait for that to finish. After `close()` has been called, further calls to `next()` ,`nextv()` or `all()` will yield an error with code [`LEVEL_ITERATOR_NOT_OPEN`](#errors). + +#### `iterator.db` + +A reference to the database that created this iterator. + +#### `iterator.count` + +Read-only getter that indicates how many keys have been yielded so far (by any method) excluding calls that errored or yielded `undefined`. + +#### `iterator.limit` + +Read-only getter that reflects the `limit` that was set in options. Greater than or equal to zero. Equals `Infinity` if no limit, which allows for easy math: + +```js +const hasMore = iterator.count < iterator.limit +const remaining = iterator.limit - iterator.count +``` + +### `keyIterator` + +A key iterator has the same interface as `iterator` except that its methods yield keys instead of entries. For the `keyIterator.next(callback)` method, this means that the `callback` will receive two arguments (an error and key) instead of three. Usage is otherwise the same. + +### `valueIterator` + +A value iterator has the same interface as `iterator` except that its methods yield values instead of entries. For the `valueIterator.next(callback)` method, this means that the `callback` will receive two arguments (an error and value) instead of three. Usage is otherwise the same. + +### `sublevel` + +A sublevel is an instance of the `AbstractSublevel` class, which extends `AbstractLevel` and thus has the same API as documented above. Sublevels have a few additional properties. + +#### `sublevel.prefix` + +Prefix of the sublevel. A read-only string property. + +```js +const example = db.sublevel('example') +const nested = example.sublevel('nested') + +console.log(example.prefix) // '!example!' +console.log(nested.prefix) // '!example!!nested!' +``` + +#### `sublevel.db` + +Parent database. A read-only property. + +```js +const example = db.sublevel('example') +const nested = example.sublevel('nested') + +console.log(example.db === db) // true +console.log(nested.db === db) // true +``` + +### Encodings + +Any method that takes a `key` argument, `value` argument or range options like `gte`, hereby jointly referred to as `data`, runs that `data` through an _encoding_. This means to encode input `data` and decode output `data`. + +[Several encodings](https://github.com/Level/transcoder#built-in-encodings) are builtin courtesy of [`level-transcoder`](https://github.com/Level/transcoder) and can be selected by a short name like `'utf8'` or `'json'`. The default encoding is `'utf8'` which ensures you'll always get back a string. Encodings can be specified for keys and values independently with `keyEncoding` and `valueEncoding` options, either in the database constructor or per method to apply an encoding selectively. For example: + +```js +const db = level('./db', { + keyEncoding: 'view', + valueEncoding: 'json' +}) + +// Use binary keys +const key = Uint8Array.from([1, 2]) + +// Encode the value with JSON +await db.put(key, { x: 2 }) + +// Decode the value with JSON. Yields { x: 2 } +const obj = await db.get(key) + +// Decode the value with utf8. Yields '{"x":2}' +const str = await db.get(key, { valueEncoding: 'utf8' }) +``` + +The `keyEncoding` and `valueEncoding` options accept a string to select a known encoding by its name, or an object to use a custom encoding like [`charwise`](https://github.com/dominictarr/charwise). See [`keyEncoding()`](#encoding--dbkeyencodingencoding) for details. If a custom encoding is passed to the database constructor, subsequent method calls can refer to that encoding by name. Supported encodings are exposed in the `db.supports` manifest: + +```js +const db = level('./db', { + keyEncoding: require('charwise'), + valueEncoding: 'json' +}) + +// Includes builtin and custom encodings +console.log(db.supports.encodings.utf8) // true +console.log(db.supports.encodings.charwise) // true +``` + +An encoding can both widen and limit the range of `data` types. The default `'utf8'` encoding can only store strings. Other types, though accepted, are irreversibly stringified before storage. That includes JavaScript primitives which are converted with [`String(x)`](https://tc39.es/ecma262/multipage/text-processing.html#sec-string-constructor-string-value), Buffer which is converted with [`x.toString('utf8')`](https://nodejs.org/api/buffer.html#buftostringencoding-start-end) and Uint8Array converted with [`TextDecoder#decode(x)`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/decode). Use other encodings for a richer set of `data` types, as well as binary data without a conversion cost - or loss of non-unicode bytes. + +For binary data two builtin encodings are available: `'buffer'` and `'view'`. They use a Buffer or Uint8Array respectively. To some extent these encodings are interchangeable, as the `'buffer'` encoding also accepts Uint8Array as input `data` (and will convert that to a Buffer without copying the underlying ArrayBuffer), the `'view'` encoding also accepts Buffer as input `data` and so forth. Output `data` will be either a Buffer or Uint8Array respectively and can also be converted: + +```js +const db = level('./db', { valueEncoding: 'view' }) +const buffer = await db.get('example', { valueEncoding: 'buffer' }) +``` + +In browser environments it may be preferable to only use `'view'`. When bundling JavaScript with Webpack, Browserify or other, you can choose not to use the `'buffer'` encoding and (through configuration of the bundler) exclude the [`buffer`](https://github.com/feross/buffer) shim in order to reduce bundle size. + +Regardless of the choice of encoding, a `key` or `value` may not be `null` or `undefined` due to preexisting significance in iterators and streams. No such restriction exists on range options because `null` and `undefined` are significant types in encodings like [`charwise`](https://github.com/dominictarr/charwise) as well as some underlying stores like IndexedDB. Consumers of an `abstract-level` implementation must assume that range options like `{ gt: undefined }` are _not_ the same as `{}`. The [abstract test suite](#test-suite) does not test these types. Whether they are supported or how they sort may differ per implementation. An implementation can choose to: + +- Encode these types to make them meaningful +- Have no defined behavior (moving the concern to a higher level) +- Delegate to an underlying database (moving the concern to a lower level). + +Lastly, one way or another, every implementation _must_ support `data` of type String and _should_ support `data` of type Buffer or Uint8Array. + +### Events + +An `abstract-level` database is an [`EventEmitter`](https://nodejs.org/api/events.html) and emits the following events. + +| Event | Description | Arguments | +| :-------- | :------------------- | :------------------- | +| `put` | Entry was updated | `key, value` (any) | +| `del` | Entry was deleted | `key` (any) | +| `batch` | Batch has executed | `operations` (array) | +| `clear` | Entries were deleted | `options` (object) | +| `opening` | Database is opening | - | +| `open` | Database has opened | - | +| `ready` | Alias for `open` | - | +| `closing` | Database is closing | - | +| `closed` | Database has closed. | - | + +For example you can do: + +```js +db.on('put', function (key, value) { + console.log('Updated', { key, value }) +}) +``` + +Any keys, values and range options in these events are the original arguments passed to the relevant operation that triggered the event, before having encoded them. + +### Errors + +Errors thrown or yielded from the methods above will have a `code` property that is an uppercase string. Error codes will not change between major versions, but error messages will. Messages may also differ between implementations; they are free and encouraged to tune messages. + +A database may also throw [`TypeError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError) errors (or other core error constructors in JavaScript) without a `code` and without any guarantee on the stability of error properties - because these errors indicate invalid arguments and other programming mistakes that should not be catched much less have associated logic. + +Error codes will be one of the following. + +#### `LEVEL_NOT_FOUND` + +When a key was not found. + +#### `LEVEL_DATABASE_NOT_OPEN` + +When an operation was made on a database while it was closing or closed. Or when a database failed to `open()` including when `close()` was called in the mean time, thus changing the eventual `status`. The error may have a `cause` property that explains a failure to open: + +```js +try { + await db.open() +} catch (err) { + console.error(err.code) // 'LEVEL_DATABASE_NOT_OPEN' + + if (err.cause && err.cause.code === 'LEVEL_LOCKED') { + // Another process or instance has opened the database + } +} +``` + +#### `LEVEL_DATABASE_NOT_CLOSED` + +When a database failed to `close()`. Including when `open()` was called in the mean time, thus changing the eventual `status`. The error may have a `cause` property that explains a failure to close. + +#### `LEVEL_ITERATOR_NOT_OPEN` + +When an operation was made on an iterator while it was closing or closed, which may also be the result of the database being closed. + +#### `LEVEL_ITERATOR_BUSY` + +When `iterator.next()` or `seek()` was called while a previous `next()` call was still in progress. + +#### `LEVEL_BATCH_NOT_OPEN` + +When an operation was made on a chained batch while it was closing or closed, which may also be the result of the database being closed or that `write()` was called on the chained batch. + +#### `LEVEL_ENCODING_NOT_FOUND` + +When a `keyEncoding` or `valueEncoding` option specified a named encoding that does not exist. + +#### `LEVEL_ENCODING_NOT_SUPPORTED` + +When a `keyEncoding` or `valueEncoding` option specified an encoding that isn't supported by the database. + +#### `LEVEL_DECODE_ERROR` + +When decoding of keys or values failed. The error _may_ have a [`cause`](https://github.com/tc39/proposal-error-cause) property containing an original error. For example, it might be a [`SyntaxError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError) from an internal [`JSON.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) call: + +```js +await db.put('key', 'invalid json', { valueEncoding: 'utf8' }) + +try { + const value = await db.get('key', { valueEncoding: 'json' }) +} catch (err) { + console.log(err.code) // 'LEVEL_DECODE_ERROR' + console.log(err.cause) // 'SyntaxError: Unexpected token i in JSON at position 0' +} +``` + +#### `LEVEL_INVALID_KEY` + +When a key is `null`, `undefined` or (if an implementation deems it so) otherwise invalid. + +#### `LEVEL_INVALID_VALUE` + +When a value is `null`, `undefined` or (if an implementation deems it so) otherwise invalid. + +#### `LEVEL_CORRUPTION` + +Data could not be read (from an underlying store) due to a corruption. + +#### `LEVEL_IO_ERROR` + +Data could not be read (from an underlying store) due to an input/output error, for example from the filesystem. + +#### `LEVEL_INVALID_PREFIX` + +When a sublevel prefix contains characters outside of the supported byte range. + +#### `LEVEL_NOT_SUPPORTED` + +When a module needs a certain feature, typically as indicated by `db.supports`, but that feature is not available on a database argument or other. For example, some kind of plugin may depend on `seek()`: + +```js +const ModuleError = require('module-error') + +module.exports = function plugin (db) { + if (!db.supports.seek) { + throw new ModuleError('Database must support seeking', { + code: 'LEVEL_NOT_SUPPORTED' + }) + } + + // .. +} +``` + +#### `LEVEL_LEGACY` + +When a method, option or other property was used that has been removed from the API. + +#### `LEVEL_LOCKED` + +When an attempt was made to open a database that is already open in another process or instance. Used by `classic-level` and other implementations of `abstract-level` that use exclusive locks. + +#### `LEVEL_READONLY` + +When an attempt was made to write data to a read-only database. Used by `many-level`. + +#### `LEVEL_CONNECTION_LOST` + +When a database relies on a connection to a remote party and that connection has been lost. Used by `many-level`. + +#### `LEVEL_REMOTE_ERROR` + +When a remote party encountered an unexpected condition that it can't reflect with a more specific code. Used by `many-level`. + +### Shared Access + +Unless documented otherwise, implementations of `abstract-level` do _not_ support accessing a database from multiple processes running in parallel. That includes Node.js clusters and Electron renderer processes. + +See [`Level/awesome`](https://github.com/Level/awesome#shared-access) for modules like [`multileveldown`](https://github.com/Level/multileveldown) and [`level-party`](https://github.com/Level/party) that allow a database to be shared across processes and/or machines. Note: at the time of writing these modules are not yet ported to `abstract-level` and thus incompatible. + +## Private API For Implementors + +To implement an `abstract-level` database, extend the [`AbstractLevel`](./abstract-level.js) class and override the private underscored versions of its methods. For example, to implement the public `put()` method, override the private `_put()` method. The same goes for other classes (some of which are optional to override). All classes can be found on the main export of the npm package: + +```js +const { + AbstractLevel, + AbstractSublevel, + AbstractIterator, + AbstractKeyIterator, + AbstractValueIterator, + AbstractChainedBatch +} = require('abstract-level') +``` + +Naming-wise, implementations should use a class name in the form of `*Level` (suffixed, for example `MemoryLevel`) and an npm package name in the form of `*-level` (for example `memory-level`). While utilities and plugins should use a package name in the form of `level-*` (prefixed). + +Each of the private methods listed below will receive exactly the number and types of arguments described, regardless of what is passed in through the public API. Public methods provide type checking: if a consumer calls `db.batch(123)` they'll get an error that the first argument must be an array. Optional arguments get sensible defaults: a `db.get(key)` call translates to a `db._get(key, options, callback)` call. + +All callbacks are error-first and must be asynchronous. If an operation within your implementation is synchronous, invoke the callback on a next tick using microtask scheduling. For convenience, instances of `AbstractLevel`, `AbstractIterator` and other classes include a `nextTick(fn, ...args)` utility method that uses [`process.nextTick()`](https://nodejs.org/api/process.html#processnexttickcallback-args) in Node.js and [`queueMicrotask()`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask) in browsers. It's recommended to exclusively use this utility (including in unit tests) as it guarantees a consistent order of operations. + +Where possible, the default private methods are sensible noops that do nothing. For example, `db._open(callback)` will merely invoke `callback` on a next tick. Other methods have functional defaults. Each method documents whether implementing it is mandatory. + +When throwing or yielding an error, prefer using a [known error code](#errors). If new codes are required for your implementation and you wish to use the `LEVEL_` prefix for consistency, feel free to open an issue to discuss. We'll likely want to document those codes here. + +### Example + +Let's implement a basic in-memory database: + +```js +const { AbstractLevel } = require('abstract-level') +const ModuleError = require('module-error') + +class ExampleLevel extends AbstractLevel { + // This in-memory example doesn't have a location argument + constructor (options) { + // Declare supported encodings + const encodings = { utf8: true } + + // Call AbstractLevel constructor + super({ encodings }, options) + + // Create a map to store entries + this._entries = new Map() + } + + _open (options, callback) { + // Here you would open any necessary resources. + // Use nextTick to be a nice async citizen + this.nextTick(callback) + } + + _put (key, value, options, callback) { + this._entries.set(key, value) + this.nextTick(callback) + } + + _get (key, options, callback) { + const value = this._entries.get(key) + + if (value === undefined) { + return this.nextTick(callback, new ModuleError(`Key ${key} was not found`, { + code: 'LEVEL_NOT_FOUND' + })) + } + + this.nextTick(callback, null, value) + } + + _del (key, options, callback) { + this._entries.delete(key) + this.nextTick(callback) + } +} +``` + +Now we can use our implementation (with either callbacks or promises): + +```js +const db = new ExampleLevel() + +await db.put('foo', 'bar') +const value = await db.get('foo') + +console.log(value) // 'bar' +``` + +Although our basic implementation only supports `'utf8'` strings internally, we do get to use [encodings](#encodings) that encode _to_ that. For example, the `'json'` encoding which encodes to `'utf8'`: + +```js +const db = new ExampleLevel({ valueEncoding: 'json' }) +await db.put('foo', { a: 123 }) +const value = await db.get('foo') + +console.log(value) // { a: 123 } +``` + +See [`memory-level`](https://github.com/Level/memory-level) if you are looking for a complete in-memory implementation. The example above notably lacks iterator support and would not pass the [abstract test suite](#test-suite). + +### `db = AbstractLevel(manifest[, options])` + +The database constructor. Sets the [`status`](#dbstatus) to `'opening'`. Takes a [manifest](https://github.com/Level/supports) object that the constructor will enrich with defaults. At minimum, the manifest must declare which `encodings` are supported in the private API. For example: + +```js +class ExampleLevel extends AbstractLevel { + constructor (location, options) { + const manifest = { + encodings: { buffer: true } + } + + // Call AbstractLevel constructor. + // Location is not handled by AbstractLevel. + super(manifest, options) + } +} +``` + +Both the public and private API of `abstract-level` are encoding-aware. This means that private methods receive `keyEncoding` and `valueEncoding` options too. Implementations don't need to perform encoding or decoding themselves. Rather, the `keyEncoding` and `valueEncoding` options are lower-level encodings that indicate the type of already-encoded input data or the expected type of yet-to-be-decoded output data. They're one of `'buffer'`, `'view'`, `'utf8'` and always strings in the private API. + +If the manifest declared support of `'buffer'`, then `keyEncoding` and `valueEncoding` will always be `'buffer'`. If the manifest declared support of `'utf8'` then `keyEncoding` and `valueEncoding` will be `'utf8'`. + +For example: a call like `await db.put(key, { x: 2 }, { valueEncoding: 'json' })` will encode the `{ x: 2 }` value and might forward it to the private API as `db._put(key, '{"x":2}', { valueEncoding: 'utf8' }, callback)`. Same for the key (omitted for brevity). + +The public API will coerce user input as necessary. If the manifest declared support of `'utf8'` then `await db.get(24)` will forward that number key as a string: `db._get('24', { keyEncoding: 'utf8', ... }, callback)`. However, this is _not_ true for output: a private API call like `db._get(key, { keyEncoding: 'utf8', valueEncoding: 'utf8' }, callback)` _must_ yield a string value to the callback. + +All private methods below that take a `key` argument, `value` argument or range option, will receive that data in encoded form. That includes `iterator._seek()` with its `target` argument. So if the manifest declared support of `'buffer'` then `db.iterator({ gt: 2 })` translates into `db._iterator({ gt: Buffer.from('2'), ...options })` and `iterator.seek(128)` translates into `iterator._seek(Buffer.from('128'), options)`. + +The `AbstractLevel` constructor will add other supported encodings to the public manifest. If the private API only supports `'buffer'`, the resulting `db.supports.encodings` will nevertheless be as follows because all other encodings can be transcoded to `'buffer'`: + +```js +{ buffer: true, view: true, utf8: true, json: true, ... } +``` + +Implementations can also declare support of multiple encodings. Keys and values will then be encoded and decoded via the most optimal path. For example, in [`classic-level`](https://github.com/Level/classic-level) (previously `leveldown`) it's: + +```js +super({ encodings: { buffer: true, utf8: true } }, options, callback) +``` + +This has the benefit that user input needs less conversion steps: if the input is a string then `classic-level` can pass that to its LevelDB binding as-is. Vice versa for output. + +### `db._open(options, callback)` + +Open the database. The `options` object will always have the following properties: `createIfMissing`, `errorIfExists`. If opening failed, call the `callback` function with an error. Otherwise call `callback` without any arguments. + +The default `_open()` is a sensible noop and invokes `callback` on a next tick. + +### `db._close(callback)` + +Close the database. When this is called, `db.status` will be `'closing'`. If closing failed, call the `callback` function with an error, which resets the `status` to `'open'`. Otherwise call `callback` without any arguments, which sets `status` to `'closed'`. Make an effort to avoid failing, or if it does happen that it is subsequently safe to keep using the database. If the database was never opened or failed to open then `_close()` will not be called. + +The default `_close()` is a sensible noop and invokes `callback` on a next tick. In native implementations (native addons written in C++ or other) it's recommended to delay closing if any operations are in flight. See [`classic-level`](https://github.com/Level/classic-level) (previously `leveldown`) for an example of this behavior. The JavaScript side in `abstract-level` will prevent _new_ operations before the database is reopened (as explained in constructor documentation above) while the C++ side should prevent closing the database before _existing_ operations have completed. + +### `db._get(key, options, callback)` + +Get a value by `key`. The `options` object will always have the following properties: `keyEncoding` and `valueEncoding`. If the key does not exist, call the `callback` function with an error that has code [`LEVEL_NOT_FOUND`](#errors). Otherwise call `callback` with `null` as the first argument and the value as the second. + +The default `_get()` invokes `callback` on a next tick with a `LEVEL_NOT_FOUND` error. It must be overridden. + +### `db._getMany(keys, options, callback)` + +Get multiple values by an array of `keys`. The `options` object will always have the following properties: `keyEncoding` and `valueEncoding`. If an error occurs, call the `callback` function with an error. Otherwise call `callback` with `null` as the first argument and an array of values as the second. If a key does not exist, set the relevant value to `undefined`. + +The default `_getMany()` invokes `callback` on a next tick with an array of values that is equal in length to `keys` and is filled with `undefined`. It must be overridden. + +### `db._put(key, value, options, callback)` + +Add a new entry or overwrite an existing entry. The `options` object will always have the following properties: `keyEncoding` and `valueEncoding`. If putting failed, call the `callback` function with an error. Otherwise call `callback` without any arguments. + +The default `_put()` invokes `callback` on a next tick. It must be overridden. + +### `db._del(key, options, callback)` + +Delete an entry. The `options` object will always have the following properties: `keyEncoding`. If deletion failed, call the `callback` function with an error. Otherwise call `callback` without any arguments. + +The default `_del()` invokes `callback` on a next tick. It must be overridden. + +### `db._batch(operations, options, callback)` + +Perform multiple _put_ and/or _del_ operations in bulk. The `operations` argument is always an array containing a list of operations to be executed sequentially, although as a whole they should be performed as an atomic operation. The `_batch()` method will not be called if the `operations` array is empty. Each operation is guaranteed to have at least `type`, `key` and `keyEncoding` properties. If the type is `put`, the operation will also have `value` and `valueEncoding` properties. There are no default options but `options` will always be an object. If the batch failed, call the `callback` function with an error. Otherwise call `callback` without any arguments. + +The public `batch()` method supports encoding options both in the `options` argument and per operation. The private `_batch()` method only receives encoding options per operation. + +The default `_batch()` invokes `callback` on a next tick. It must be overridden. + +### `db._chainedBatch()` + +The default `_chainedBatch()` returns a functional `AbstractChainedBatch` instance that uses `db._batch(array, options, callback)` under the hood. To implement chained batch in an optimized manner, extend `AbstractChainedBatch` and return an instance of this class in the `_chainedBatch()` method: + +```js +const { AbstractChainedBatch } = require('abstract-level') + +class ExampleChainedBatch extends AbstractChainedBatch { + constructor (db) { + super(db) + } +} + +class ExampleLevel extends AbstractLevel { + _chainedBatch () { + return new ExampleChainedBatch(this) + } +} +``` + +### `db._iterator(options)` + +The default `_iterator()` returns a noop `AbstractIterator` instance. It must be overridden, by extending `AbstractIterator` and returning an instance of this class in the `_iterator(options)` method: + +```js +const { AbstractIterator } = require('abstract-level') + +class ExampleIterator extends AbstractIterator { + constructor (db, options) { + super(db, options) + } + + // .. +} + +class ExampleLevel extends AbstractLevel { + _iterator (options) { + return new ExampleIterator(this, options) + } +} +``` + +The `options` object will always have the following properties: `reverse`, `keys`, `values`, `limit`, `keyEncoding` and `valueEncoding`. The `limit` will always be an integer, greater than or equal to `-1` and less than `Infinity`. If the user passed range options to `db.iterator()`, those will be encoded and set in `options`. + +### `db._keys(options)` + +The default `_keys()` returns a functional iterator that wraps `db._iterator()` in order to map entries to keys. For optimal performance it can be overridden by extending `AbstractKeyIterator`: + +```js +const { AbstractKeyIterator } = require('abstract-level') + +class ExampleKeyIterator extends AbstractKeyIterator { + constructor (db, options) { + super(db, options) + } + + // .. +} + +class ExampleLevel extends AbstractLevel { + _keys (options) { + return new ExampleKeyIterator(this, options) + } +} +``` + +The `options` object will always have the following properties: `reverse`, `limit` and `keyEncoding`. The `limit` will always be an integer, greater than or equal to `-1` and less than `Infinity`. If the user passed range options to `db.keys()`, those will be encoded and set in `options`. + +### `db._values(options)` + +The default `_values()` returns a functional iterator that wraps `db._iterator()` in order to map entries to values. For optimal performance it can be overridden by extending `AbstractValueIterator`: + +```js +const { AbstractValueIterator } = require('abstract-level') + +class ExampleValueIterator extends AbstractValueIterator { + constructor (db, options) { + super(db, options) + } + + // .. +} + +class ExampleLevel extends AbstractLevel { + _values (options) { + return new ExampleValueIterator(this, options) + } +} +``` + +The `options` object will always have the following properties: `reverse`, `limit`, `keyEncoding` and `valueEncoding`. The `limit` will always be an integer, greater than or equal to -1 and less than Infinity. If the user passed range options to `db.values()`, those will be encoded and set in `options`. + +### `db._clear(options, callback)` + +Delete all entries or a range. Does not have to be atomic. It is recommended (and possibly mandatory in the future) to operate on a snapshot so that writes scheduled after a call to `clear()` will not be affected. + +Implementations that wrap another database can typically forward the `_clear()` call to that database, having transformed range options if necessary. + +The `options` object will always have the following properties: `reverse`, `limit` and `keyEncoding`. If the user passed range options to `db.clear()`, those will be encoded and set in `options`. + +### `sublevel = db._sublevel(name, options)` + +Create a [sublevel](#sublevel). The `options` object will always have the following properties: `separator`. The default `_sublevel()` returns a new instance of the [`AbstractSublevel`](./lib/abstract-sublevel.js) class. Overriding is optional. The `AbstractSublevel` can be extended in order to add additional methods to sublevels: + +```js +const { AbstractLevel, AbstractSublevel } = require('abstract-level') + +class ExampleLevel extends AbstractLevel { + _sublevel (name, options) { + return new ExampleSublevel(this, name, options) + } +} + +// For brevity this does not handle deferred open +class ExampleSublevel extends AbstractSublevel { + example (key, options) { + // Encode and prefix the key + const keyEncoding = this.keyEncoding(options.keyEncoding) + const keyFormat = keyEncoding.format + + key = this.prefixKey(keyEncoding.encode(key), keyFormat) + + // The parent database can be accessed like so. Make sure + // to forward encoding options and use the full key. + this.db.del(key, { keyEncoding: keyFormat }, ...) + } +} +``` + +### `iterator = AbstractIterator(db, options)` + +The first argument to this constructor must be an instance of the relevant `AbstractLevel` implementation. The constructor will set `iterator.db` which is used (among other things) to access encodings and ensures that `db` will not be garbage collected in case there are no other references to it. The `options` argument must be the original `options` object that was passed to `db._iterator()` and it is therefore not (publicly) possible to create an iterator via constructors alone. + +#### `iterator._next(callback)` + +Advance to the next entry and yield that entry. If the operation failed, call the `callback` function with an error. Otherwise, call `callback` with `null`, a `key` and a `value`. If a `limit` was set and the iterator already yielded that many entries (via any of the methods) then `_next()` will not be called. + +The default `_next()` invokes `callback` on a next tick. It must be overridden. + +#### `iterator._nextv(size, options, callback)` + +Advance repeatedly and get at most `size` amount of entries in a single call. The `size` argument will always be an integer greater than 0. If a `limit` was set then `size` will be at most `limit - iterator.count`. If a `limit` was set and the iterator already yielded that many entries (via any of the methods) then `_nextv()` will not be called. There are no default options but `options` will always be an object. If the operation failed, call the `callback` function with an error. Otherwise, call `callback` with `null` and an array of entries. An empty array signifies the natural end of the iterator, so yield an array with at least one entry if the end has not been reached yet. + +The default `_nextv()` is a functional default that makes repeated calls to `_next()` and should be overridden for better performance. + +#### `iterator._all(options, callback)` + +Advance repeatedly and get all (remaining) entries as an array. If a `limit` was set and the iterator already yielded that many entries (via any of the methods) then `_all()` will not be called. Do not call `close()` here because `all()` will do so (regardless of any error) and this may become an opt-out behavior in the future. There are no default options but `options` will always be an object. If the operation failed, call the `callback` function with an error. Otherwise, call `callback` with `null` and an array of entries. + +The default `_all()` is a functional default that makes repeated calls to `_nextv()` and should be overridden for better performance. + +#### `iterator._seek(target, options)` + +Seek to the key closest to `target`. The `options` object will always have the following properties: `keyEncoding`. This method is optional. The default will throw an error with code [`LEVEL_NOT_SUPPORTED`](#errors). If supported, set `db.supports.seek` to `true` (via the manifest passed to the database constructor) which also enables relevant tests in the [test suite](#test-suite). + +#### `iterator._close(callback)` + +Free up underlying resources. This method is guaranteed to only be called once. Once closing is done, call `callback` without any arguments. It is not allowed to yield an error. + +The default `_close()` invokes `callback` on a next tick. Overriding is optional. + +### `keyIterator = AbstractKeyIterator(db, options)` + +A key iterator has the same interface and constructor arguments as `AbstractIterator` except that it must yields keys instead of entries. For the `keyIterator._next(callback)` method, this means that the `callback` must be given two arguments (an error and key) instead of three. The same goes for value iterators: + +```js +class ExampleKeyIterator extends AbstractKeyIterator { + _next (callback) { + this.nextTick(callback, null, 'key') + } +} + +class ExampleValueIterator extends AbstractValueIterator { + _next (callback) { + this.nextTick(callback, null, 'value') + } +} +``` + +The `options` argument must be the original `options` object that was passed to `db._keys()` and it is therefore not (publicly) possible to create a key iterator via constructors alone. The same goes for value iterators via `db._values()`. + +**Note:** the `AbstractKeyIterator` and `AbstractValueIterator` classes do _not_ extend the `AbstractIterator` class. Similarly, if your implementation overrides `db._keys()` returning a custom subclass of `AbstractKeyIterator`, then that subclass must implement methods like `_next()` separately from your subclass of `AbstractIterator`. + +### `valueIterator = AbstractValueIterator(db, options)` + +A value iterator has the same interface and constructor arguments as `AbstractIterator` except that it must yields values instead of entries. For further details, see `keyIterator` above. + +### `chainedBatch = AbstractChainedBatch(db)` + +The first argument to this constructor must be an instance of the relevant `AbstractLevel` implementation. The constructor will set `chainedBatch.db` which is used (among other things) to access encodings and ensures that `db` will not be garbage collected in case there are no other references to it. + +#### `chainedBatch._put(key, value, options)` + +Queue a `put` operation on this batch. The `options` object will always have the following properties: `keyEncoding` and `valueEncoding`. + +#### `chainedBatch._del(key, options)` + +Queue a `del` operation on this batch. The `options` object will always have the following properties: `keyEncoding`. + +#### `chainedBatch._clear()` + +Clear all queued operations on this batch. + +#### `chainedBatch._write(options, callback)` + +The default `_write` method uses `db._batch`. If the `_write` method is overridden it must atomically commit the queued operations. There are no default options but `options` will always be an object. If committing fails, call the `callback` function with an error. Otherwise call `callback` without any arguments. The `_write()` method will not be called if the chained batch has zero queued operations. + +#### `chainedBatch._close(callback)` + +Free up underlying resources. This method is guaranteed to only be called once. Once closing is done, call `callback` without any arguments. It is not allowed to yield an error. + +The default `_close()` invokes `callback` on a next tick. Overriding is optional. + +## Test Suite + +To prove that your implementation is `abstract-level` compliant, include the abstract test suite in your `test.js` (or similar): + +```js +const test = require('tape') +const suite = require('abstract-level/test') +const ExampleLevel = require('.') + +suite({ + test, + factory (options) { + return new ExampleLevel(options) + } +}) +``` + +The `test` option _must_ be a function that is API-compatible with [`tape`](https://github.com/substack/tape). The `factory` option _must_ be a function that returns a unique and isolated instance of your implementation. The factory will be called many times by the test suite. + +If your implementation is disk-based we recommend using [`tempy`](https://github.com/sindresorhus/tempy) (or similar) to create unique temporary directories. Your setup could look something like: + +```js +const test = require('tape') +const tempy = require('tempy') +const suite = require('abstract-level/test') +const ExampleLevel = require('.') + +suite({ + test, + factory (options) { + return new ExampleLevel(tempy.directory(), options) + } +}) +``` + +### Excluding tests + +As not every implementation can be fully compliant due to limitations of its underlying storage, some tests may be skipped. This must be done via `db.supports` which is set via the constructor. For example, to skip snapshot tests: + +```js +const { AbstractLevel } = require('abstract-level') + +class ExampleLevel extends AbstractLevel { + constructor (location, options) { + super({ snapshots: false }, options) + } +} +``` + +This also serves as a signal to users of your implementation. + +### Reusing `testCommon` + +The input to the test suite is a `testCommon` object. Should you need to reuse `testCommon` for your own (additional) tests, use the included utility to create a `testCommon` with defaults: + +```js +const test = require('tape') +const suite = require('abstract-level/test') +const ExampleLevel = require('.') + +const testCommon = suite.common({ + test, + factory (options) { + return new ExampleLevel(options) + } +}) + +suite(testCommon) +``` + +The `testCommon` object will have the `test` and `factory` properties described above, as well as a convenience `supports` property that is lazily copied from a `factory().supports`. You might use it like so: + +```js +test('custom test', function (t) { + const db = testCommon.factory() + // .. +}) + +testCommon.supports.seek && test('another test', function (t) { + const db = testCommon.factory() + // .. +}) +``` + +## Spread The Word + +If you'd like to share your awesome implementation with the world, here's what you might want to do: + +- Add an awesome badge to your `README`: `![level badge](https://leveljs.org/img/badge.svg)` +- Publish your awesome module to [npm](https://npmjs.org) +- Send a Pull Request to [Level/awesome](https://github.com/Level/awesome) to advertise your work! + +## Install + +With [npm](https://npmjs.org) do: + +``` +npm install abstract-level +``` + +## Contributing + +[`Level/abstract-level`](https://github.com/Level/abstract-level) is an **OPEN Open Source Project**. This means that: + +> Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project. + +See the [Contribution Guide](https://github.com/Level/community/blob/master/CONTRIBUTING.md) for more details. + +## Donate + +Support us with a monthly donation on [Open Collective](https://opencollective.com/level) and help us continue our work. + +## License + +[MIT](LICENSE) + +[level-badge]: https://leveljs.org/img/badge.svg diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/UPGRADING.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/UPGRADING.md new file mode 100644 index 0000000..5b82feb --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/UPGRADING.md @@ -0,0 +1,479 @@ +# Upgrade Guide + +This document describes breaking changes and how to upgrade. For a complete list of changes including minor and patch releases, please refer to the [changelog](CHANGELOG.md). + +## Table of Contents + +
Click to expand + +- [1.0.0](#100) + - [1. API parity with `levelup`](#1-api-parity-with-levelup) + - [1.1. New: promises](#11-new-promises) + - [1.2. New: events](#12-new-events) + - [1.3. New: idempotent open](#13-new-idempotent-open) + - [1.4. New: deferred open](#14-new-deferred-open) + - [1.5. No constructor callback](#15-no-constructor-callback) + - [1.6. New: state checks](#16-new-state-checks) + - [1.7. New: chained batch length](#17-new-chained-batch-length) + - [2. API parity with `level`](#2-api-parity-with-level) + - [2.1. For consumers](#21-for-consumers) + - [2.2. For implementors](#22-for-implementors) + - [2.3. Other notable changes](#23-other-notable-changes) + - [3. Streams have moved](#3-streams-have-moved) + - [4. Zero-length keys and range options are now valid](#4-zero-length-keys-and-range-options-are-now-valid) + - [5. Resources are auto-closed](#5-resources-are-auto-closed) + - [5.1. Closing iterators is idempotent](#51-closing-iterators-is-idempotent) + - [5.2. Chained batch can be closed](#52-chained-batch-can-be-closed) + - [6. Errors now use codes](#6-errors-now-use-codes) + - [7. Semi-private properties have been removed](#7-semi-private-properties-have-been-removed) + - [8. Changes to test suite](#8-changes-to-test-suite) + - [9. Sublevels are builtin](#9-sublevels-are-builtin) + +
+ +## 1.0.0 + +**Introducing `abstract-level`: a fork of [`abstract-leveldown`](https://github.com/Level/abstract-leveldown) that removes the need for [`levelup`](https://github.com/Level/levelup), [`encoding-down`](https://github.com/Level/encoding-down) and more. An `abstract-level` database is a complete solution that doesn't need to be wrapped. It has the same API as `level(up)` including encodings, promises and events. In addition, implementations can now choose to use Uint8Array instead of Buffer. Consumers of an implementation can use both. Sublevels are builtin.** + +We've put together several upgrade guides for different modules. See the [FAQ](https://github.com/Level/community#faq) to find the best upgrade guide for you. This upgrade guide describes how to replace `abstract-leveldown` with `abstract-level`. Implementations that do so, can no longer be wrapped with `levelup`. + +The npm package name is `abstract-level` and the main export is called `AbstractLevel` rather than `AbstractLevelDOWN`. It started using classes. Support of Node.js 10 has been dropped. + +For most folks, a database that upgraded from `abstract-leveldown` to `abstract-level` can be a drop-in replacement for a `level(up)` database (with the exception of stream methods). Let's start this guide there: all methods have been enhanced to reach API parity with `levelup` and `level`. + +### 1. API parity with `levelup` + +#### 1.1. New: promises + +Methods that take a callback now also support promises. They return a promise if no callback is provided, the same as `levelup`. Implementations that override public (non-underscored) methods _must_ do the same and any implementation _should_ do the same for additional methods if any. + +#### 1.2. New: events + +An `abstract-level` database emits the same events as `levelup` would. + +#### 1.3. New: idempotent open + +Opening and closing a database is idempotent and safe, similar to `levelup` but more precise. If `open()` and `close()` are called repeatedly, the last call dictates the final status. Callbacks are not called (or promises not resolved) until any pending state changes are done. Same for events. Unlike on `levelup` it is safe to call `open()` while status is `'closing'`: the database will wait for closing to complete and then reopen. None of these changes are likely to constitute a breaking change; they increase state consistency in edge cases. + +The `open()` method has a new option called `passive`. If set to `true` the call will wait for, but not initiate, opening of the database. To similar effect as `db.once('open', callback)` with added benefit that it also works if the database is already open. Implementations that wrap another database can use the `passive` option to open themselves without taking full control of the database that they wrap. + +#### 1.4. New: deferred open + +Deferred open is built-in. This means a database opens itself a tick after its constructor returns (unless `open()` was called manually). Any operations made until opening has completed are queued up in memory. When opening completes the operations are replayed. If opening failed (and this is a new behavior compared to `levelup`) the operations will yield errors. The `AbstractLevel` class has a new `defer()` method for an implementation to defer custom operations. + +The initial `status` of a database is `'opening'` rather than `'new'`, which no longer exists. Wrapping a database with [`deferred-leveldown`](https://github.com/Level/deferred-leveldown) is not supported and will exhibit undefined behavior. + +Implementations must also accept options for `open()` in their constructor, which was previously done by `levelup`. For example, usage of the [`classic-level`](https://github.com/Level/classic-level) implementation is as follows: + +```js +const db = new ClassicLevel('./db', { + createIfMissing: false, + compression: false +}) +``` + +This works by first forwarding options to the `AbstractLevel` constructor, which in turn forwards them to `open(options)`. If `open(options)` is called manually those options will be shallowly merged with options from the constructor: + +```js +// Results in { createIfMissing: false, compression: true } +await db.open({ compression: true }) +``` + +A database is not "patch-safe". If some form of plugin monkey-patches a database like in the following example, it must now also take the responsibility of deferring the operation (as well as handling promises and callbacks) using `db.defer()`. I.e. this example is incomplete: + +```js +function plugin (db) { + const original = db.get + + db.get = function (...args) { + original.call(this, ...args) + } +} +``` + +#### 1.5. No constructor callback + +The database constructor does not take a callback argument, unlike `levelup`. This goes for `abstract-level` as well as implementations - which is to say, implementors don't have to (and should not) support this old pattern. + +Instead call `db.open()` if you wish to wait for opening (which is not necessary to use the database) or to capture an error. If that's your reason for using the callback and you previously initialized a database like so (simplified): + +```js +levelup(function (err, db) { + // .. +}) +``` + +You must now do: + +```js +db.open(function (err) { + // .. +}) +``` + +Or using promises: + +```js +await db.open() +``` + +#### 1.6. New: state checks + +On any operation, an `abstract-level` database checks if it's open. If not, it will either throw an error (if the relevant API is synchronous) or asynchronously yield an error. For example: + +```js +await db.close() + +try { + db.iterator() +} catch (err) { + console.log(err.code) // LEVEL_DATABASE_NOT_OPEN +} +``` + +_Errors now have a `code` property. More on that below\._ + +This may be a breaking change downstream because it changes error messages for implementations that had their own safety checks (which will now be ineffective because `abstract-level` checks are performed first) or implicitly relied on `levelup` checks. By safety we mean mainly that yielding a JavaScript error is preferred over segmentation faults, though non-native implementations also benefit from detecting incorrect usage. + +Implementations that have additional methods should add or align their own safety checks for consistency. Like so: + +
+Click to expand + +```js +const ModuleError = require('module-error') + +class ExampleLevel extends AbstractLevel { + // For brevity this example does not implement promises or encodings + approximateSize (start, end, callback) { + if (this.status === 'opening') { + this.defer(() => this.approximateSize(start, end, callback)) + } else if (this.status !== 'open') { + this.nextTick(callback, new ModuleError('Database is not open', { + code: 'LEVEL_DATABASE_NOT_OPEN' + })) + } else { + // .. + } + } +} +``` + +
+ +#### 1.7. New: chained batch length + +The `AbstractChainedBatch` prototype has a new `length` property that, like a chained batch in `levelup`, returns the number of queued operations in the batch. Implementations should not have to make changes for this unless they monkey-patched public methods of `AbstractChainedBatch`. + +### 2. API parity with `level` + +It was previously necessary to use [`level`](https://github.com/Level/level) to get the "full experience". Or similar modules like [`level-mem`](https://github.com/Level/mem), [`level-rocksdb`](https://github.com/Level/level-rocksdb) and more. These modules combined an `abstract-leveldown` implementation with [`encoding-down`](https://github.com/Level/encoding-down) and [`levelup`](https://github.com/Level/levelup). Encodings are now built-in to `abstract-level`, using [`level-transcoder`](https://github.com/Level/transcoder) rather than [`level-codec`](https://github.com/Level/codec). The main change is that logic from the existing public API has been expanded down into the storage layer. + +The `level` module still has a place, for its support of both Node.js and browsers and for being the main entrypoint into the Level ecosystem. The next major version of `level`, that's v8.0.0, will likely simply export [`classic-level`](https://github.com/Level/classic-level) in Node.js and [`browser-level`](https://github.com/Level/browser-level) in browsers. To differentiate, the text below will refer to the old version as `level@7`. + +#### 2.1. For consumers + +All relevant methods including the database constructor now accept `keyEncoding` and `valueEncoding` options, the same as `level@7`. Read operations now yield strings rather than buffers by default, having the same default `'utf8'` encoding as `level@7` and friends. + +There are a few differences from `level@7` and `encoding-down`. Some breaking: + +- The lesser-used `'ascii'`, `'ucs2'` and `'utf16le'` encodings are not supported +- The `'id'` encoding, which was not supported by any active `abstract-leveldown` implementation and aliased as `'none'`, has been removed +- The undocumented `encoding` option (as an alias for `valueEncoding`) is not supported. + +And some non-breaking: + +- The `'binary'` encoding has been renamed to `'buffer'`, with `'binary'` as an alias +- The `'utf8'` encoding previously did not touch Buffers. Now it will call `buffer.toString('utf8')` for consistency. Consumers can use the `'buffer'` encoding to avoid this conversion. + +If you previously did one of the following (on a database that's defaulting to the `'utf8'` encoding): + +```js +await db.put('a', Buffer.from('x')) +await db.put('a', Buffer.from('x'), { valueEncoding: 'binary' }) +``` + +Both examples will still work (assuming the buffer contains only UTF8 data) but you should now do: + +```js +await db.put('a', Buffer.from('x'), { valueEncoding: 'buffer' }) +``` + +Or use the new `'view'` encoding which accepts Uint8Arrays (and therefore also Buffer): + +```js +await db.put('a', new Uint8Array(...), { valueEncoding: 'view' }) +``` + +#### 2.2. For implementors + +_You can skip this section if you're consuming (rather than writing) an `abstract-level` implementation._ + +Both the public and private API of `abstract-level` are encoding-aware. This means that private methods receive `keyEncoding` and `valueEncoding` options too, instead of the `keyAsBuffer`, `valueAsBuffer` and `asBuffer` options that `abstract-leveldown` had. Implementations don't need to perform encoding or decoding themselves. In fact they can do less: the `_serializeKey()` and `_serializeValue()` methods are also gone and implementations are less likely to have to convert between strings and buffers. + +For example: a call like `db.put(key, { x: 2 }, { valueEncoding: 'json' })` will encode the `{ x: 2 }` value and might forward it to the private API as `db._put(key, '{"x":2}', { valueEncoding: 'utf8' }, callback)`. Same for the key, omitted for brevity. We say "might" because it depends on the implementation, which can now declare which encodings it supports. + +To first give a concrete example for `get()`, if your implementation previously did: + +```js +class ExampleLeveldown extends AbstractLevelDOWN { + _get (key, options, callback) { + if (options.asBuffer) { + this.nextTick(callback, null, Buffer.from('abc')) + } else { + this.nextTick(callback, null, 'abc') + } + } +} +``` + +You must now do (if still relevant): + +```js +class ExampleLevel extends AbstractLevel { + _get (key, options, callback) { + if (options.valueEncoding === 'buffer') { + this.nextTick(callback, null, Buffer.from('abc')) + } else { + this.nextTick(callback, null, 'abc') + } + } +} +``` + +The encoding options and data received by the private API depend on which encodings it supports. It must declare those via the manifest passed to the `AbstractLevel` constructor. See the [`README`](README.md) for details. For example, an implementation might only support storing data as Uint8Arrays, known here as the `'view'` encoding: + +```js +class ExampleLevel extends AbstractLevel { + constructor (location, options) { + super({ encodings: { view: true } }, options) + } +} +``` + +The earlier `put()` example would then result in `db._put(key, value, { valueEncoding: 'view' }, callback)` where `value` is a Uint8Array containing JSON in binary form. And the earlier `_get()` example can be simplified to: + +```js +class ExampleLevel extends AbstractLevel { + _get (key, options, callback) { + // No need to check valueEncoding as it's always 'view' + this.nextTick(callback, null, new Uint8Array(...)) + } +} +``` + +Implementations can also declare support of multiple encodings; keys and values will then be encoded via the most optimal path. For example: + +```js +super({ + encodings: { + view: true, + utf8: true + } +}) +``` + +#### 2.3. Other notable changes + +- The `AbstractIterator` constructor now requires an `options` argument, for encoding options +- The `AbstractIterator#_seek()` method got a new `options` argument, for a `keyEncoding` option +- The `db.supports.bufferKeys` property has been removed. Use `db.supports.encodings.buffer` instead. + +### 3. Streams have moved + +Node.js readable streams must now be created with a new standalone module called [`level-read-stream`](https://github.com/Level/read-stream), rather than database methods like `db.createReadStream()`. Please see its [upgrade guide](https://github.com/Level/read-stream/blob/main/UPGRADING.md#100) for details. + +To offer an alternative to `db.createKeyStream()` and `db.createValueStream()`, two new types of iterators have been added: `db.keys()` and `db.values()`. Their default implementations are functional but implementors may want to override them for optimal performance. The same goes for two new methods on iterators: `nextv()` and `all()`. To achieve this and honor the `limit` option, abstract iterators now count how many items they yielded, which may remove the need for implementations to do so on their own. Please see the README for details. + +### 4. Zero-length keys and range options are now valid + +These keys sort before anything else. Historically they weren't supported for causing segmentation faults in `leveldown`. That doesn't apply to today's codebase. Implementations must now support: + +```js +await db.put('', 'example') + +console.log(await db.get('')) // 'example' + +for await (const [key, value] of db.iterator({ lte: '' })) { + console.log(value) // 'example' +} +``` + +Same goes for zero-length Buffer and Uint8Array keys. Zero-length keys would previously result in an error and never reach the private API. + +### 5. Resources are auto-closed + +To further improve safety and consistency, additional changes were made that make an `abstract-level` database safer to use than `abstract-leveldown` wrapped with `levelup`. + +#### 5.1. Closing iterators is idempotent + +The `iterator.end()` method has been renamed to `iterator.close()`, with `end()` being an alias until a next major version in the future. The term "close" makes it easier to differentiate between the iterator having reached its natural end (data-wise) versus closing it to cleanup resources. If you previously did: + +```js +const iterator = db.iterator() +iterator.end(callback) +``` + +You should now do one of: + +```js +iterator.close(callback) +await iterator.close() +``` + +Likewise, in the private API for implementors, `_end()` has been renamed to `_close()` but without an alias. This method is no longer allowed to yield an error. + +On `db.close()`, non-closed iterators are now automatically closed. This may be a breaking change but only if an implementation has (at its own risk) overridden the public `end()` method, because `close()` or `end()` is now an idempotent operation rather than yielding an `end() already called on iterator` error. If a `next()` call is in progress, closing the iterator (or database) will wait for that. + +The error message `cannot call next() after end()` has been replaced with code `LEVEL_ITERATOR_NOT_OPEN`, the error `cannot call seek() after end()` has been removed in favor of a silent return, and `cannot call next() before previous next() has completed` and `cannot call seek() before next() has completed` have been replaced with code `LEVEL_ITERATOR_BUSY`. + +The `next()` method no longer returns `this` (when a callback is provided). + +#### 5.2. Chained batch can be closed + +Chained batch has a new method `close()` which is an idempotent operation and automatically called after `write()` (for backwards compatibility) or on `db.close()`. This to ensure batches can't be used after closing and reopening a db. If a `write()` is in progress, closing will wait for that. If `write()` is never called then `close()` must be. For example: + +```js +const batch = db.batch() + .put('abc', 'zyz') + .del('foo') + +if (someCondition) { + await batch.write() +} else { + // Decided not to commit + await batch.close() +} + +// In either case this will throw +batch.put('more', 'data') +``` + +These changes could be breaking for an implementation that has (at its own risk) overridden the public `write()` method. In addition, the error message `write() already called on this batch` has been replaced with code `LEVEL_BATCH_NOT_OPEN`. + +An implementation can optionally override `AbstractChainedBatch#_close()` if it has resources to free and wishes to free them earlier than GC would. + +### 6. Errors now use codes + +The [`level-errors`](https://github.com/Level/errors) module as used by `levelup` and friends, is not used or exposed by `abstract-level`. Instead errors thrown or yielded from a database have a `code` property. See the [`README`](./README.md#errors) for details. Going forward, the semver contract will be on `code` and error messages will change without a semver-major bump. + +To minimize breakage, the most used error as yielded by `get()` when an entry is not found, has the same properties that `level-errors` added (`notFound` and `status`) in addition to code `LEVEL_NOT_FOUND`. Those properties will be removed in a future version. Implementations can still yield an error that matches `/NotFound/i.test(err)` or they can start using the code. Either way `abstract-level` will normalize the error. + +If you previously did: + +```js +db.get('abc', function (err, value) { + if (err && err.notFound) { + // Handle missing entry + } +}) +``` + +That will still work but it's preferred to do: + +```js +db.get('abc', function (err, value) { + if (err && err.code === 'LEVEL_NOT_FOUND') { + // Handle missing entry + } +}) +``` + +Or using promises: + +```js +try { + const value = await db.get('abc') +} catch (err) { + if (err.code === 'LEVEL_NOT_FOUND') { + // Handle missing entry + } +} +``` + +### 7. Semi-private properties have been removed + +The following properties and methods can no longer be accessed, as they've been removed or replaced with internal [symbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol): + +- `AbstractIterator#_nexting` +- `AbstractIterator#_ended` +- `AbstractChainedBatch#_written` +- `AbstractChainedBatch#_checkWritten()` +- `AbstractChainedBatch#_operations` +- `AbstractLevel#_setupIteratorOptions()` + +### 8. Changes to test suite + +_You can skip this section if you're consuming (rather than writing) an `abstract-level` implementation._ + +The abstract test suite of `abstract-level` has some breaking changes compared to `abstract-leveldown`: + +- Options to skip tests have been removed in favor of `db.supports` +- Support of `db.clear()` and `db.getMany()` is now mandatory. The default (slow) implementation of `_clear()` has been removed. +- Added tests that `gte` and `lte` range options take precedence over `gt` and `lt` respectively. This is incompatible with [`ltgt`](https://github.com/dominictarr/ltgt) but aligns with `subleveldown`, [`level-option-wrap`](https://github.com/substack/level-option-wrap) and half of `leveldown`. There was no good choice. +- The `setUp` and `tearDown` functions have been removed from the test suite and `suite.common()`. +- Added ability to access manifests via `testCommon.supports`, by lazily copying it from `testCommon.factory().supports`. This requires that the manifest does not change during the lifetime of a `db`. +- Your `factory()` function must now accept an `options` argument. + +Many tests were imported from `levelup`, `encoding-down`, `deferred-leveldown`, `memdown`, `level-js` and `leveldown`. They test the changes described above and improve coverage of existing behavior. + +Lastly, it's recommended to revisit any custom tests of an implementation. In particular if those tests relied upon the previously loose state checking of `abstract-leveldown`. For example, making a `db.put()` call before `db.open()`. Such a test now has a different meaning. The previous meaning can typically be restored by inserting `db.once('open', ...)` or `await db.open()` logic. + +### 9. Sublevels are builtin + +_This section is only relevant if you use [`subleveldown`](https://github.com/Level/subleveldown) (which can not wrap an `abstract-level` database)._ + +Sublevels are now builtin. If you previously did: + +```js +const sub = require('subleveldown') +const example1 = sub(db, 'example1') +const example2 = sub(db, 'example2', { valueEncoding: 'json' }) +``` + +You must now do: + +```js +const example1 = db.sublevel('example1') +const example2 = db.sublevel('example2', { valueEncoding: 'json' }) +``` + +The key structure is equal to that of `subleveldown`. This means that an `abstract-level` sublevel can read sublevels previously created with (and populated by) `subleveldown`. There are some new features: + +- `db.batch(..)` takes a `sublevel` option on operations, to atomically commit data to multiple sublevels +- Sublevels support Uint8Array in addition to Buffer +- `AbstractLevel#_sublevel()` can be overridden to add additional methods to sublevels. + +To reduce function overloads, the prefix argument (`example1` above) is now required and it's called `name` here. If you previously did one of the following, resulting in an empty name: + +```js +subleveldown(db) +subleveldown(db, { separator: '@' }) +``` + +You must now use an explicit empty name: + +```js +db.sublevel('') +db.sublevel('', { separator: '@' }) +``` + +The string shorthand for `{ separator }` has also been removed. If you previously did: + +```js +subleveldown(db, 'example', '@') +``` + +You must now do: + +```js +db.sublevel('example', { separator: '@' }) +``` + +Third, the `open` option has been removed. If you need an asynchronous open hook, feel free to open an issue to discuss restoring this API. Should it support promises? Should `abstract-level` support it on any database and not just sublevels? + +Lastly, the error message `Parent database is not open` (courtesy of `subleveldown` which had to check open state to prevent segmentation faults from underlying databases) changed to error code [`LEVEL_DATABASE_NOT_OPEN`](https://github.com/Level/abstract-level#errors) (courtesy of `abstract-level` which does those checks on any database). + +--- + +_For earlier releases, before `abstract-level` was forked from `abstract-leveldown` (v7.2.0), please see [the upgrade guide of `abstract-leveldown`](https://github.com/Level/abstract-leveldown/blob/master/UPGRADING.md)._ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/abstract-chained-batch.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/abstract-chained-batch.js new file mode 100644 index 0000000..3f77b3c --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/abstract-chained-batch.js @@ -0,0 +1,181 @@ +'use strict' + +const { fromCallback } = require('catering') +const ModuleError = require('module-error') +const { getCallback, getOptions } = require('./lib/common') + +const kPromise = Symbol('promise') +const kStatus = Symbol('status') +const kOperations = Symbol('operations') +const kFinishClose = Symbol('finishClose') +const kCloseCallbacks = Symbol('closeCallbacks') + +class AbstractChainedBatch { + constructor (db) { + if (typeof db !== 'object' || db === null) { + const hint = db === null ? 'null' : typeof db + throw new TypeError(`The first argument must be an abstract-level database, received ${hint}`) + } + + this[kOperations] = [] + this[kCloseCallbacks] = [] + this[kStatus] = 'open' + this[kFinishClose] = this[kFinishClose].bind(this) + + this.db = db + this.db.attachResource(this) + this.nextTick = db.nextTick + } + + get length () { + return this[kOperations].length + } + + put (key, value, options) { + if (this[kStatus] !== 'open') { + throw new ModuleError('Batch is not open: cannot call put() after write() or close()', { + code: 'LEVEL_BATCH_NOT_OPEN' + }) + } + + const err = this.db._checkKey(key) || this.db._checkValue(value) + if (err) throw err + + const db = options && options.sublevel != null ? options.sublevel : this.db + const original = options + const keyEncoding = db.keyEncoding(options && options.keyEncoding) + const valueEncoding = db.valueEncoding(options && options.valueEncoding) + const keyFormat = keyEncoding.format + + // Forward encoding options + options = { ...options, keyEncoding: keyFormat, valueEncoding: valueEncoding.format } + + // Prevent double prefixing + if (db !== this.db) { + options.sublevel = null + } + + const mappedKey = db.prefixKey(keyEncoding.encode(key), keyFormat) + const mappedValue = valueEncoding.encode(value) + + this._put(mappedKey, mappedValue, options) + this[kOperations].push({ ...original, type: 'put', key, value }) + + return this + } + + _put (key, value, options) {} + + del (key, options) { + if (this[kStatus] !== 'open') { + throw new ModuleError('Batch is not open: cannot call del() after write() or close()', { + code: 'LEVEL_BATCH_NOT_OPEN' + }) + } + + const err = this.db._checkKey(key) + if (err) throw err + + const db = options && options.sublevel != null ? options.sublevel : this.db + const original = options + const keyEncoding = db.keyEncoding(options && options.keyEncoding) + const keyFormat = keyEncoding.format + + // Forward encoding options + options = { ...options, keyEncoding: keyFormat } + + // Prevent double prefixing + if (db !== this.db) { + options.sublevel = null + } + + this._del(db.prefixKey(keyEncoding.encode(key), keyFormat), options) + this[kOperations].push({ ...original, type: 'del', key }) + + return this + } + + _del (key, options) {} + + clear () { + if (this[kStatus] !== 'open') { + throw new ModuleError('Batch is not open: cannot call clear() after write() or close()', { + code: 'LEVEL_BATCH_NOT_OPEN' + }) + } + + this._clear() + this[kOperations] = [] + + return this + } + + _clear () {} + + write (options, callback) { + callback = getCallback(options, callback) + callback = fromCallback(callback, kPromise) + options = getOptions(options) + + if (this[kStatus] !== 'open') { + this.nextTick(callback, new ModuleError('Batch is not open: cannot call write() after write() or close()', { + code: 'LEVEL_BATCH_NOT_OPEN' + })) + } else if (this.length === 0) { + this.close(callback) + } else { + this[kStatus] = 'writing' + this._write(options, (err) => { + this[kStatus] = 'closing' + this[kCloseCallbacks].push(() => callback(err)) + + // Emit after setting 'closing' status, because event may trigger a + // db close which in turn triggers (idempotently) closing this batch. + if (!err) this.db.emit('batch', this[kOperations]) + + this._close(this[kFinishClose]) + }) + } + + return callback[kPromise] + } + + _write (options, callback) {} + + close (callback) { + callback = fromCallback(callback, kPromise) + + if (this[kStatus] === 'closing') { + this[kCloseCallbacks].push(callback) + } else if (this[kStatus] === 'closed') { + this.nextTick(callback) + } else { + this[kCloseCallbacks].push(callback) + + if (this[kStatus] !== 'writing') { + this[kStatus] = 'closing' + this._close(this[kFinishClose]) + } + } + + return callback[kPromise] + } + + _close (callback) { + this.nextTick(callback) + } + + [kFinishClose] () { + this[kStatus] = 'closed' + this.db.detachResource(this) + + const callbacks = this[kCloseCallbacks] + this[kCloseCallbacks] = [] + + for (const cb of callbacks) { + cb() + } + } +} + +exports.AbstractChainedBatch = AbstractChainedBatch diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/abstract-iterator.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/abstract-iterator.js new file mode 100644 index 0000000..48330ec --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/abstract-iterator.js @@ -0,0 +1,490 @@ +'use strict' + +const { fromCallback } = require('catering') +const ModuleError = require('module-error') +const { getOptions, getCallback } = require('./lib/common') + +const kPromise = Symbol('promise') +const kCallback = Symbol('callback') +const kWorking = Symbol('working') +const kHandleOne = Symbol('handleOne') +const kHandleMany = Symbol('handleMany') +const kAutoClose = Symbol('autoClose') +const kFinishWork = Symbol('finishWork') +const kReturnMany = Symbol('returnMany') +const kClosing = Symbol('closing') +const kHandleClose = Symbol('handleClose') +const kClosed = Symbol('closed') +const kCloseCallbacks = Symbol('closeCallbacks') +const kKeyEncoding = Symbol('keyEncoding') +const kValueEncoding = Symbol('valueEncoding') +const kAbortOnClose = Symbol('abortOnClose') +const kLegacy = Symbol('legacy') +const kKeys = Symbol('keys') +const kValues = Symbol('values') +const kLimit = Symbol('limit') +const kCount = Symbol('count') + +const emptyOptions = Object.freeze({}) +const noop = () => {} +let warnedEnd = false + +// This class is an internal utility for common functionality between AbstractIterator, +// AbstractKeyIterator and AbstractValueIterator. It's not exported. +class CommonIterator { + constructor (db, options, legacy) { + if (typeof db !== 'object' || db === null) { + const hint = db === null ? 'null' : typeof db + throw new TypeError(`The first argument must be an abstract-level database, received ${hint}`) + } + + if (typeof options !== 'object' || options === null) { + throw new TypeError('The second argument must be an options object') + } + + this[kClosed] = false + this[kCloseCallbacks] = [] + this[kWorking] = false + this[kClosing] = false + this[kAutoClose] = false + this[kCallback] = null + this[kHandleOne] = this[kHandleOne].bind(this) + this[kHandleMany] = this[kHandleMany].bind(this) + this[kHandleClose] = this[kHandleClose].bind(this) + this[kKeyEncoding] = options[kKeyEncoding] + this[kValueEncoding] = options[kValueEncoding] + this[kLegacy] = legacy + this[kLimit] = Number.isInteger(options.limit) && options.limit >= 0 ? options.limit : Infinity + this[kCount] = 0 + + // Undocumented option to abort pending work on close(). Used by the + // many-level module as a temporary solution to a blocked close(). + // TODO (next major): consider making this the default behavior. Native + // implementations should have their own logic to safely close iterators. + this[kAbortOnClose] = !!options.abortOnClose + + this.db = db + this.db.attachResource(this) + this.nextTick = db.nextTick + } + + get count () { + return this[kCount] + } + + get limit () { + return this[kLimit] + } + + next (callback) { + let promise + + if (callback === undefined) { + promise = new Promise((resolve, reject) => { + callback = (err, key, value) => { + if (err) reject(err) + else if (!this[kLegacy]) resolve(key) + else if (key === undefined && value === undefined) resolve() + else resolve([key, value]) + } + }) + } else if (typeof callback !== 'function') { + throw new TypeError('Callback must be a function') + } + + if (this[kClosing]) { + this.nextTick(callback, new ModuleError('Iterator is not open: cannot call next() after close()', { + code: 'LEVEL_ITERATOR_NOT_OPEN' + })) + } else if (this[kWorking]) { + this.nextTick(callback, new ModuleError('Iterator is busy: cannot call next() until previous call has completed', { + code: 'LEVEL_ITERATOR_BUSY' + })) + } else { + this[kWorking] = true + this[kCallback] = callback + + if (this[kCount] >= this[kLimit]) this.nextTick(this[kHandleOne], null) + else this._next(this[kHandleOne]) + } + + return promise + } + + _next (callback) { + this.nextTick(callback) + } + + nextv (size, options, callback) { + callback = getCallback(options, callback) + callback = fromCallback(callback, kPromise) + options = getOptions(options, emptyOptions) + + if (!Number.isInteger(size)) { + this.nextTick(callback, new TypeError("The first argument 'size' must be an integer")) + return callback[kPromise] + } + + if (this[kClosing]) { + this.nextTick(callback, new ModuleError('Iterator is not open: cannot call nextv() after close()', { + code: 'LEVEL_ITERATOR_NOT_OPEN' + })) + } else if (this[kWorking]) { + this.nextTick(callback, new ModuleError('Iterator is busy: cannot call nextv() until previous call has completed', { + code: 'LEVEL_ITERATOR_BUSY' + })) + } else { + if (size < 1) size = 1 + if (this[kLimit] < Infinity) size = Math.min(size, this[kLimit] - this[kCount]) + + this[kWorking] = true + this[kCallback] = callback + + if (size <= 0) this.nextTick(this[kHandleMany], null, []) + else this._nextv(size, options, this[kHandleMany]) + } + + return callback[kPromise] + } + + _nextv (size, options, callback) { + const acc = [] + const onnext = (err, key, value) => { + if (err) { + return callback(err) + } else if (this[kLegacy] ? key === undefined && value === undefined : key === undefined) { + return callback(null, acc) + } + + acc.push(this[kLegacy] ? [key, value] : key) + + if (acc.length === size) { + callback(null, acc) + } else { + this._next(onnext) + } + } + + this._next(onnext) + } + + all (options, callback) { + callback = getCallback(options, callback) + callback = fromCallback(callback, kPromise) + options = getOptions(options, emptyOptions) + + if (this[kClosing]) { + this.nextTick(callback, new ModuleError('Iterator is not open: cannot call all() after close()', { + code: 'LEVEL_ITERATOR_NOT_OPEN' + })) + } else if (this[kWorking]) { + this.nextTick(callback, new ModuleError('Iterator is busy: cannot call all() until previous call has completed', { + code: 'LEVEL_ITERATOR_BUSY' + })) + } else { + this[kWorking] = true + this[kCallback] = callback + this[kAutoClose] = true + + if (this[kCount] >= this[kLimit]) this.nextTick(this[kHandleMany], null, []) + else this._all(options, this[kHandleMany]) + } + + return callback[kPromise] + } + + _all (options, callback) { + // Must count here because we're directly calling _nextv() + let count = this[kCount] + const acc = [] + + const nextv = () => { + // Not configurable, because implementations should optimize _all(). + const size = this[kLimit] < Infinity ? Math.min(1e3, this[kLimit] - count) : 1e3 + + if (size <= 0) { + this.nextTick(callback, null, acc) + } else { + this._nextv(size, emptyOptions, onnextv) + } + } + + const onnextv = (err, items) => { + if (err) { + callback(err) + } else if (items.length === 0) { + callback(null, acc) + } else { + acc.push.apply(acc, items) + count += items.length + nextv() + } + } + + nextv() + } + + [kFinishWork] () { + const cb = this[kCallback] + + // Callback will be null if work was aborted on close + if (this[kAbortOnClose] && cb === null) return noop + + this[kWorking] = false + this[kCallback] = null + + if (this[kClosing]) this._close(this[kHandleClose]) + + return cb + } + + [kReturnMany] (cb, err, items) { + if (this[kAutoClose]) { + this.close(cb.bind(null, err, items)) + } else { + cb(err, items) + } + } + + seek (target, options) { + options = getOptions(options, emptyOptions) + + if (this[kClosing]) { + // Don't throw here, to be kind to implementations that wrap + // another db and don't necessarily control when the db is closed + } else if (this[kWorking]) { + throw new ModuleError('Iterator is busy: cannot call seek() until next() has completed', { + code: 'LEVEL_ITERATOR_BUSY' + }) + } else { + const keyEncoding = this.db.keyEncoding(options.keyEncoding || this[kKeyEncoding]) + const keyFormat = keyEncoding.format + + if (options.keyEncoding !== keyFormat) { + options = { ...options, keyEncoding: keyFormat } + } + + const mapped = this.db.prefixKey(keyEncoding.encode(target), keyFormat) + this._seek(mapped, options) + } + } + + _seek (target, options) { + throw new ModuleError('Iterator does not support seek()', { + code: 'LEVEL_NOT_SUPPORTED' + }) + } + + close (callback) { + callback = fromCallback(callback, kPromise) + + if (this[kClosed]) { + this.nextTick(callback) + } else if (this[kClosing]) { + this[kCloseCallbacks].push(callback) + } else { + this[kClosing] = true + this[kCloseCallbacks].push(callback) + + if (!this[kWorking]) { + this._close(this[kHandleClose]) + } else if (this[kAbortOnClose]) { + // Don't wait for work to finish. Subsequently ignore the result. + const cb = this[kFinishWork]() + + cb(new ModuleError('Aborted on iterator close()', { + code: 'LEVEL_ITERATOR_NOT_OPEN' + })) + } + } + + return callback[kPromise] + } + + _close (callback) { + this.nextTick(callback) + } + + [kHandleClose] () { + this[kClosed] = true + this.db.detachResource(this) + + const callbacks = this[kCloseCallbacks] + this[kCloseCallbacks] = [] + + for (const cb of callbacks) { + cb() + } + } + + async * [Symbol.asyncIterator] () { + try { + let item + + while ((item = (await this.next())) !== undefined) { + yield item + } + } finally { + if (!this[kClosed]) await this.close() + } + } +} + +// For backwards compatibility this class is not (yet) called AbstractEntryIterator. +class AbstractIterator extends CommonIterator { + constructor (db, options) { + super(db, options, true) + this[kKeys] = options.keys !== false + this[kValues] = options.values !== false + } + + [kHandleOne] (err, key, value) { + const cb = this[kFinishWork]() + if (err) return cb(err) + + try { + key = this[kKeys] && key !== undefined ? this[kKeyEncoding].decode(key) : undefined + value = this[kValues] && value !== undefined ? this[kValueEncoding].decode(value) : undefined + } catch (err) { + return cb(new IteratorDecodeError('entry', err)) + } + + if (!(key === undefined && value === undefined)) { + this[kCount]++ + } + + cb(null, key, value) + } + + [kHandleMany] (err, entries) { + const cb = this[kFinishWork]() + if (err) return this[kReturnMany](cb, err) + + try { + for (const entry of entries) { + const key = entry[0] + const value = entry[1] + + entry[0] = this[kKeys] && key !== undefined ? this[kKeyEncoding].decode(key) : undefined + entry[1] = this[kValues] && value !== undefined ? this[kValueEncoding].decode(value) : undefined + } + } catch (err) { + return this[kReturnMany](cb, new IteratorDecodeError('entries', err)) + } + + this[kCount] += entries.length + this[kReturnMany](cb, null, entries) + } + + end (callback) { + if (!warnedEnd && typeof console !== 'undefined') { + warnedEnd = true + console.warn(new ModuleError( + 'The iterator.end() method was renamed to close() and end() is an alias that will be removed in a future version', + { code: 'LEVEL_LEGACY' } + )) + } + + return this.close(callback) + } +} + +class AbstractKeyIterator extends CommonIterator { + constructor (db, options) { + super(db, options, false) + } + + [kHandleOne] (err, key) { + const cb = this[kFinishWork]() + if (err) return cb(err) + + try { + key = key !== undefined ? this[kKeyEncoding].decode(key) : undefined + } catch (err) { + return cb(new IteratorDecodeError('key', err)) + } + + if (key !== undefined) this[kCount]++ + cb(null, key) + } + + [kHandleMany] (err, keys) { + const cb = this[kFinishWork]() + if (err) return this[kReturnMany](cb, err) + + try { + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + keys[i] = key !== undefined ? this[kKeyEncoding].decode(key) : undefined + } + } catch (err) { + return this[kReturnMany](cb, new IteratorDecodeError('keys', err)) + } + + this[kCount] += keys.length + this[kReturnMany](cb, null, keys) + } +} + +class AbstractValueIterator extends CommonIterator { + constructor (db, options) { + super(db, options, false) + } + + [kHandleOne] (err, value) { + const cb = this[kFinishWork]() + if (err) return cb(err) + + try { + value = value !== undefined ? this[kValueEncoding].decode(value) : undefined + } catch (err) { + return cb(new IteratorDecodeError('value', err)) + } + + if (value !== undefined) this[kCount]++ + cb(null, value) + } + + [kHandleMany] (err, values) { + const cb = this[kFinishWork]() + if (err) return this[kReturnMany](cb, err) + + try { + for (let i = 0; i < values.length; i++) { + const value = values[i] + values[i] = value !== undefined ? this[kValueEncoding].decode(value) : undefined + } + } catch (err) { + return this[kReturnMany](cb, new IteratorDecodeError('values', err)) + } + + this[kCount] += values.length + this[kReturnMany](cb, null, values) + } +} + +// Internal utility, not typed or exported +class IteratorDecodeError extends ModuleError { + constructor (subject, cause) { + super(`Iterator could not decode ${subject}`, { + code: 'LEVEL_DECODE_ERROR', + cause + }) + } +} + +// To help migrating to abstract-level +for (const k of ['_ended property', '_nexting property', '_end method']) { + Object.defineProperty(AbstractIterator.prototype, k.split(' ')[0], { + get () { throw new ModuleError(`The ${k} has been removed`, { code: 'LEVEL_LEGACY' }) }, + set () { throw new ModuleError(`The ${k} has been removed`, { code: 'LEVEL_LEGACY' }) } + }) +} + +// Exposed so that AbstractLevel can set these options +AbstractIterator.keyEncoding = kKeyEncoding +AbstractIterator.valueEncoding = kValueEncoding + +exports.AbstractIterator = AbstractIterator +exports.AbstractKeyIterator = AbstractKeyIterator +exports.AbstractValueIterator = AbstractValueIterator diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/abstract-level.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/abstract-level.js new file mode 100644 index 0000000..82878ea --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/abstract-level.js @@ -0,0 +1,818 @@ +'use strict' + +const { supports } = require('level-supports') +const { Transcoder } = require('level-transcoder') +const { EventEmitter } = require('events') +const { fromCallback } = require('catering') +const ModuleError = require('module-error') +const { AbstractIterator } = require('./abstract-iterator') +const { DefaultKeyIterator, DefaultValueIterator } = require('./lib/default-kv-iterator') +const { DeferredIterator, DeferredKeyIterator, DeferredValueIterator } = require('./lib/deferred-iterator') +const { DefaultChainedBatch } = require('./lib/default-chained-batch') +const { getCallback, getOptions } = require('./lib/common') +const rangeOptions = require('./lib/range-options') + +const kPromise = Symbol('promise') +const kLanded = Symbol('landed') +const kResources = Symbol('resources') +const kCloseResources = Symbol('closeResources') +const kOperations = Symbol('operations') +const kUndefer = Symbol('undefer') +const kDeferOpen = Symbol('deferOpen') +const kOptions = Symbol('options') +const kStatus = Symbol('status') +const kDefaultOptions = Symbol('defaultOptions') +const kTranscoder = Symbol('transcoder') +const kKeyEncoding = Symbol('keyEncoding') +const kValueEncoding = Symbol('valueEncoding') +const noop = () => {} + +class AbstractLevel extends EventEmitter { + constructor (manifest, options) { + super() + + if (typeof manifest !== 'object' || manifest === null) { + throw new TypeError("The first argument 'manifest' must be an object") + } + + options = getOptions(options) + const { keyEncoding, valueEncoding, passive, ...forward } = options + + this[kResources] = new Set() + this[kOperations] = [] + this[kDeferOpen] = true + this[kOptions] = forward + this[kStatus] = 'opening' + + this.supports = supports(manifest, { + status: true, + promises: true, + clear: true, + getMany: true, + deferredOpen: true, + + // TODO (next major): add seek + snapshots: manifest.snapshots !== false, + permanence: manifest.permanence !== false, + + // TODO: remove from level-supports because it's always supported + keyIterator: true, + valueIterator: true, + iteratorNextv: true, + iteratorAll: true, + + encodings: manifest.encodings || {}, + events: Object.assign({}, manifest.events, { + opening: true, + open: true, + closing: true, + closed: true, + put: true, + del: true, + batch: true, + clear: true + }) + }) + + this[kTranscoder] = new Transcoder(formats(this)) + this[kKeyEncoding] = this[kTranscoder].encoding(keyEncoding || 'utf8') + this[kValueEncoding] = this[kTranscoder].encoding(valueEncoding || 'utf8') + + // Add custom and transcoder encodings to manifest + for (const encoding of this[kTranscoder].encodings()) { + if (!this.supports.encodings[encoding.commonName]) { + this.supports.encodings[encoding.commonName] = true + } + } + + this[kDefaultOptions] = { + empty: Object.freeze({}), + entry: Object.freeze({ + keyEncoding: this[kKeyEncoding].commonName, + valueEncoding: this[kValueEncoding].commonName + }), + key: Object.freeze({ + keyEncoding: this[kKeyEncoding].commonName + }) + } + + // Let subclass finish its constructor + this.nextTick(() => { + if (this[kDeferOpen]) { + this.open({ passive: false }, noop) + } + }) + } + + get status () { + return this[kStatus] + } + + keyEncoding (encoding) { + return this[kTranscoder].encoding(encoding != null ? encoding : this[kKeyEncoding]) + } + + valueEncoding (encoding) { + return this[kTranscoder].encoding(encoding != null ? encoding : this[kValueEncoding]) + } + + open (options, callback) { + callback = getCallback(options, callback) + callback = fromCallback(callback, kPromise) + + options = { ...this[kOptions], ...getOptions(options) } + + options.createIfMissing = options.createIfMissing !== false + options.errorIfExists = !!options.errorIfExists + + const maybeOpened = (err) => { + if (this[kStatus] === 'closing' || this[kStatus] === 'opening') { + // Wait until pending state changes are done + this.once(kLanded, err ? () => maybeOpened(err) : maybeOpened) + } else if (this[kStatus] !== 'open') { + callback(new ModuleError('Database is not open', { + code: 'LEVEL_DATABASE_NOT_OPEN', + cause: err + })) + } else { + callback() + } + } + + if (options.passive) { + if (this[kStatus] === 'opening') { + this.once(kLanded, maybeOpened) + } else { + this.nextTick(maybeOpened) + } + } else if (this[kStatus] === 'closed' || this[kDeferOpen]) { + this[kDeferOpen] = false + this[kStatus] = 'opening' + this.emit('opening') + + this._open(options, (err) => { + if (err) { + this[kStatus] = 'closed' + + // Resources must be safe to close in any db state + this[kCloseResources](() => { + this.emit(kLanded) + maybeOpened(err) + }) + + this[kUndefer]() + return + } + + this[kStatus] = 'open' + this[kUndefer]() + this.emit(kLanded) + + // Only emit public event if pending state changes are done + if (this[kStatus] === 'open') this.emit('open') + + // TODO (next major): remove this alias + if (this[kStatus] === 'open') this.emit('ready') + + maybeOpened() + }) + } else if (this[kStatus] === 'open') { + this.nextTick(maybeOpened) + } else { + this.once(kLanded, () => this.open(options, callback)) + } + + return callback[kPromise] + } + + _open (options, callback) { + this.nextTick(callback) + } + + close (callback) { + callback = fromCallback(callback, kPromise) + + const maybeClosed = (err) => { + if (this[kStatus] === 'opening' || this[kStatus] === 'closing') { + // Wait until pending state changes are done + this.once(kLanded, err ? maybeClosed(err) : maybeClosed) + } else if (this[kStatus] !== 'closed') { + callback(new ModuleError('Database is not closed', { + code: 'LEVEL_DATABASE_NOT_CLOSED', + cause: err + })) + } else { + callback() + } + } + + if (this[kStatus] === 'open') { + this[kStatus] = 'closing' + this.emit('closing') + + const cancel = (err) => { + this[kStatus] = 'open' + this[kUndefer]() + this.emit(kLanded) + maybeClosed(err) + } + + this[kCloseResources](() => { + this._close((err) => { + if (err) return cancel(err) + + this[kStatus] = 'closed' + this[kUndefer]() + this.emit(kLanded) + + // Only emit public event if pending state changes are done + if (this[kStatus] === 'closed') this.emit('closed') + + maybeClosed() + }) + }) + } else if (this[kStatus] === 'closed') { + this.nextTick(maybeClosed) + } else { + this.once(kLanded, () => this.close(callback)) + } + + return callback[kPromise] + } + + [kCloseResources] (callback) { + if (this[kResources].size === 0) { + return this.nextTick(callback) + } + + let pending = this[kResources].size + let sync = true + + const next = () => { + if (--pending === 0) { + // We don't have tests for generic resources, so dezalgo + if (sync) this.nextTick(callback) + else callback() + } + } + + // In parallel so that all resources know they are closed + for (const resource of this[kResources]) { + resource.close(next) + } + + sync = false + this[kResources].clear() + } + + _close (callback) { + this.nextTick(callback) + } + + get (key, options, callback) { + callback = getCallback(options, callback) + callback = fromCallback(callback, kPromise) + options = getOptions(options, this[kDefaultOptions].entry) + + if (this[kStatus] === 'opening') { + this.defer(() => this.get(key, options, callback)) + return callback[kPromise] + } + + if (maybeError(this, callback)) { + return callback[kPromise] + } + + const err = this._checkKey(key) + + if (err) { + this.nextTick(callback, err) + return callback[kPromise] + } + + const keyEncoding = this.keyEncoding(options.keyEncoding) + const valueEncoding = this.valueEncoding(options.valueEncoding) + const keyFormat = keyEncoding.format + const valueFormat = valueEncoding.format + + // Forward encoding options to the underlying store + if (options.keyEncoding !== keyFormat || options.valueEncoding !== valueFormat) { + // Avoid spread operator because of https://bugs.chromium.org/p/chromium/issues/detail?id=1204540 + options = Object.assign({}, options, { keyEncoding: keyFormat, valueEncoding: valueFormat }) + } + + this._get(this.prefixKey(keyEncoding.encode(key), keyFormat), options, (err, value) => { + if (err) { + // Normalize not found error for backwards compatibility with abstract-leveldown and level(up) + if (err.code === 'LEVEL_NOT_FOUND' || err.notFound || /NotFound/i.test(err)) { + if (!err.code) err.code = 'LEVEL_NOT_FOUND' // Preferred way going forward + if (!err.notFound) err.notFound = true // Same as level-errors + if (!err.status) err.status = 404 // Same as level-errors + } + + return callback(err) + } + + try { + value = valueEncoding.decode(value) + } catch (err) { + return callback(new ModuleError('Could not decode value', { + code: 'LEVEL_DECODE_ERROR', + cause: err + })) + } + + callback(null, value) + }) + + return callback[kPromise] + } + + _get (key, options, callback) { + this.nextTick(callback, new Error('NotFound')) + } + + getMany (keys, options, callback) { + callback = getCallback(options, callback) + callback = fromCallback(callback, kPromise) + options = getOptions(options, this[kDefaultOptions].entry) + + if (this[kStatus] === 'opening') { + this.defer(() => this.getMany(keys, options, callback)) + return callback[kPromise] + } + + if (maybeError(this, callback)) { + return callback[kPromise] + } + + if (!Array.isArray(keys)) { + this.nextTick(callback, new TypeError("The first argument 'keys' must be an array")) + return callback[kPromise] + } + + if (keys.length === 0) { + this.nextTick(callback, null, []) + return callback[kPromise] + } + + const keyEncoding = this.keyEncoding(options.keyEncoding) + const valueEncoding = this.valueEncoding(options.valueEncoding) + const keyFormat = keyEncoding.format + const valueFormat = valueEncoding.format + + // Forward encoding options + if (options.keyEncoding !== keyFormat || options.valueEncoding !== valueFormat) { + options = Object.assign({}, options, { keyEncoding: keyFormat, valueEncoding: valueFormat }) + } + + const mappedKeys = new Array(keys.length) + + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + const err = this._checkKey(key) + + if (err) { + this.nextTick(callback, err) + return callback[kPromise] + } + + mappedKeys[i] = this.prefixKey(keyEncoding.encode(key), keyFormat) + } + + this._getMany(mappedKeys, options, (err, values) => { + if (err) return callback(err) + + try { + for (let i = 0; i < values.length; i++) { + if (values[i] !== undefined) { + values[i] = valueEncoding.decode(values[i]) + } + } + } catch (err) { + return callback(new ModuleError(`Could not decode one or more of ${values.length} value(s)`, { + code: 'LEVEL_DECODE_ERROR', + cause: err + })) + } + + callback(null, values) + }) + + return callback[kPromise] + } + + _getMany (keys, options, callback) { + this.nextTick(callback, null, new Array(keys.length).fill(undefined)) + } + + put (key, value, options, callback) { + callback = getCallback(options, callback) + callback = fromCallback(callback, kPromise) + options = getOptions(options, this[kDefaultOptions].entry) + + if (this[kStatus] === 'opening') { + this.defer(() => this.put(key, value, options, callback)) + return callback[kPromise] + } + + if (maybeError(this, callback)) { + return callback[kPromise] + } + + const err = this._checkKey(key) || this._checkValue(value) + + if (err) { + this.nextTick(callback, err) + return callback[kPromise] + } + + const keyEncoding = this.keyEncoding(options.keyEncoding) + const valueEncoding = this.valueEncoding(options.valueEncoding) + const keyFormat = keyEncoding.format + const valueFormat = valueEncoding.format + + // Forward encoding options + if (options.keyEncoding !== keyFormat || options.valueEncoding !== valueFormat) { + options = Object.assign({}, options, { keyEncoding: keyFormat, valueEncoding: valueFormat }) + } + + const mappedKey = this.prefixKey(keyEncoding.encode(key), keyFormat) + const mappedValue = valueEncoding.encode(value) + + this._put(mappedKey, mappedValue, options, (err) => { + if (err) return callback(err) + this.emit('put', key, value) + callback() + }) + + return callback[kPromise] + } + + _put (key, value, options, callback) { + this.nextTick(callback) + } + + del (key, options, callback) { + callback = getCallback(options, callback) + callback = fromCallback(callback, kPromise) + options = getOptions(options, this[kDefaultOptions].key) + + if (this[kStatus] === 'opening') { + this.defer(() => this.del(key, options, callback)) + return callback[kPromise] + } + + if (maybeError(this, callback)) { + return callback[kPromise] + } + + const err = this._checkKey(key) + + if (err) { + this.nextTick(callback, err) + return callback[kPromise] + } + + const keyEncoding = this.keyEncoding(options.keyEncoding) + const keyFormat = keyEncoding.format + + // Forward encoding options + if (options.keyEncoding !== keyFormat) { + options = Object.assign({}, options, { keyEncoding: keyFormat }) + } + + this._del(this.prefixKey(keyEncoding.encode(key), keyFormat), options, (err) => { + if (err) return callback(err) + this.emit('del', key) + callback() + }) + + return callback[kPromise] + } + + _del (key, options, callback) { + this.nextTick(callback) + } + + batch (operations, options, callback) { + if (!arguments.length) { + if (this[kStatus] === 'opening') return new DefaultChainedBatch(this) + if (this[kStatus] !== 'open') { + throw new ModuleError('Database is not open', { + code: 'LEVEL_DATABASE_NOT_OPEN' + }) + } + return this._chainedBatch() + } + + if (typeof operations === 'function') callback = operations + else callback = getCallback(options, callback) + + callback = fromCallback(callback, kPromise) + options = getOptions(options, this[kDefaultOptions].empty) + + if (this[kStatus] === 'opening') { + this.defer(() => this.batch(operations, options, callback)) + return callback[kPromise] + } + + if (maybeError(this, callback)) { + return callback[kPromise] + } + + if (!Array.isArray(operations)) { + this.nextTick(callback, new TypeError("The first argument 'operations' must be an array")) + return callback[kPromise] + } + + if (operations.length === 0) { + this.nextTick(callback) + return callback[kPromise] + } + + const mapped = new Array(operations.length) + const { keyEncoding: ke, valueEncoding: ve, ...forward } = options + + for (let i = 0; i < operations.length; i++) { + if (typeof operations[i] !== 'object' || operations[i] === null) { + this.nextTick(callback, new TypeError('A batch operation must be an object')) + return callback[kPromise] + } + + const op = Object.assign({}, operations[i]) + + if (op.type !== 'put' && op.type !== 'del') { + this.nextTick(callback, new TypeError("A batch operation must have a type property that is 'put' or 'del'")) + return callback[kPromise] + } + + const err = this._checkKey(op.key) + + if (err) { + this.nextTick(callback, err) + return callback[kPromise] + } + + const db = op.sublevel != null ? op.sublevel : this + const keyEncoding = db.keyEncoding(op.keyEncoding || ke) + const keyFormat = keyEncoding.format + + op.key = db.prefixKey(keyEncoding.encode(op.key), keyFormat) + op.keyEncoding = keyFormat + + if (op.type === 'put') { + const valueErr = this._checkValue(op.value) + + if (valueErr) { + this.nextTick(callback, valueErr) + return callback[kPromise] + } + + const valueEncoding = db.valueEncoding(op.valueEncoding || ve) + + op.value = valueEncoding.encode(op.value) + op.valueEncoding = valueEncoding.format + } + + // Prevent double prefixing + if (db !== this) { + op.sublevel = null + } + + mapped[i] = op + } + + this._batch(mapped, forward, (err) => { + if (err) return callback(err) + this.emit('batch', operations) + callback() + }) + + return callback[kPromise] + } + + _batch (operations, options, callback) { + this.nextTick(callback) + } + + sublevel (name, options) { + return this._sublevel(name, AbstractSublevel.defaults(options)) + } + + _sublevel (name, options) { + return new AbstractSublevel(this, name, options) + } + + prefixKey (key, keyFormat) { + return key + } + + clear (options, callback) { + callback = getCallback(options, callback) + callback = fromCallback(callback, kPromise) + options = getOptions(options, this[kDefaultOptions].empty) + + if (this[kStatus] === 'opening') { + this.defer(() => this.clear(options, callback)) + return callback[kPromise] + } + + if (maybeError(this, callback)) { + return callback[kPromise] + } + + const original = options + const keyEncoding = this.keyEncoding(options.keyEncoding) + + options = rangeOptions(options, keyEncoding) + options.keyEncoding = keyEncoding.format + + if (options.limit === 0) { + this.nextTick(callback) + } else { + this._clear(options, (err) => { + if (err) return callback(err) + this.emit('clear', original) + callback() + }) + } + + return callback[kPromise] + } + + _clear (options, callback) { + this.nextTick(callback) + } + + iterator (options) { + const keyEncoding = this.keyEncoding(options && options.keyEncoding) + const valueEncoding = this.valueEncoding(options && options.valueEncoding) + + options = rangeOptions(options, keyEncoding) + options.keys = options.keys !== false + options.values = options.values !== false + + // We need the original encoding options in AbstractIterator in order to decode data + options[AbstractIterator.keyEncoding] = keyEncoding + options[AbstractIterator.valueEncoding] = valueEncoding + + // Forward encoding options to private API + options.keyEncoding = keyEncoding.format + options.valueEncoding = valueEncoding.format + + if (this[kStatus] === 'opening') { + return new DeferredIterator(this, options) + } else if (this[kStatus] !== 'open') { + throw new ModuleError('Database is not open', { + code: 'LEVEL_DATABASE_NOT_OPEN' + }) + } + + return this._iterator(options) + } + + _iterator (options) { + return new AbstractIterator(this, options) + } + + keys (options) { + // Also include valueEncoding (though unused) because we may fallback to _iterator() + const keyEncoding = this.keyEncoding(options && options.keyEncoding) + const valueEncoding = this.valueEncoding(options && options.valueEncoding) + + options = rangeOptions(options, keyEncoding) + + // We need the original encoding options in AbstractKeyIterator in order to decode data + options[AbstractIterator.keyEncoding] = keyEncoding + options[AbstractIterator.valueEncoding] = valueEncoding + + // Forward encoding options to private API + options.keyEncoding = keyEncoding.format + options.valueEncoding = valueEncoding.format + + if (this[kStatus] === 'opening') { + return new DeferredKeyIterator(this, options) + } else if (this[kStatus] !== 'open') { + throw new ModuleError('Database is not open', { + code: 'LEVEL_DATABASE_NOT_OPEN' + }) + } + + return this._keys(options) + } + + _keys (options) { + return new DefaultKeyIterator(this, options) + } + + values (options) { + const keyEncoding = this.keyEncoding(options && options.keyEncoding) + const valueEncoding = this.valueEncoding(options && options.valueEncoding) + + options = rangeOptions(options, keyEncoding) + + // We need the original encoding options in AbstractValueIterator in order to decode data + options[AbstractIterator.keyEncoding] = keyEncoding + options[AbstractIterator.valueEncoding] = valueEncoding + + // Forward encoding options to private API + options.keyEncoding = keyEncoding.format + options.valueEncoding = valueEncoding.format + + if (this[kStatus] === 'opening') { + return new DeferredValueIterator(this, options) + } else if (this[kStatus] !== 'open') { + throw new ModuleError('Database is not open', { + code: 'LEVEL_DATABASE_NOT_OPEN' + }) + } + + return this._values(options) + } + + _values (options) { + return new DefaultValueIterator(this, options) + } + + defer (fn) { + if (typeof fn !== 'function') { + throw new TypeError('The first argument must be a function') + } + + this[kOperations].push(fn) + } + + [kUndefer] () { + if (this[kOperations].length === 0) { + return + } + + const operations = this[kOperations] + this[kOperations] = [] + + for (const op of operations) { + op() + } + } + + // TODO: docs and types + attachResource (resource) { + if (typeof resource !== 'object' || resource === null || + typeof resource.close !== 'function') { + throw new TypeError('The first argument must be a resource object') + } + + this[kResources].add(resource) + } + + // TODO: docs and types + detachResource (resource) { + this[kResources].delete(resource) + } + + _chainedBatch () { + return new DefaultChainedBatch(this) + } + + _checkKey (key) { + if (key === null || key === undefined) { + return new ModuleError('Key cannot be null or undefined', { + code: 'LEVEL_INVALID_KEY' + }) + } + } + + _checkValue (value) { + if (value === null || value === undefined) { + return new ModuleError('Value cannot be null or undefined', { + code: 'LEVEL_INVALID_VALUE' + }) + } + } +} + +// Expose browser-compatible nextTick for dependents +// TODO: after we drop node 10, also use queueMicrotask in node +AbstractLevel.prototype.nextTick = require('./lib/next-tick') + +const { AbstractSublevel } = require('./lib/abstract-sublevel')({ AbstractLevel }) + +exports.AbstractLevel = AbstractLevel +exports.AbstractSublevel = AbstractSublevel + +const maybeError = function (db, callback) { + if (db[kStatus] !== 'open') { + db.nextTick(callback, new ModuleError('Database is not open', { + code: 'LEVEL_DATABASE_NOT_OPEN' + })) + return true + } + + return false +} + +const formats = function (db) { + return Object.keys(db.supports.encodings) + .filter(k => !!db.supports.encodings[k]) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/index.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/index.d.ts new file mode 100644 index 0000000..3cdcc8c --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/index.d.ts @@ -0,0 +1,42 @@ +export { + AbstractLevel, + AbstractDatabaseOptions, + AbstractOpenOptions, + AbstractGetOptions, + AbstractGetManyOptions, + AbstractPutOptions, + AbstractDelOptions, + AbstractBatchOptions, + AbstractBatchOperation, + AbstractBatchPutOperation, + AbstractBatchDelOperation, + AbstractClearOptions +} from './types/abstract-level' + +export { + AbstractIterator, + AbstractIteratorOptions, + AbstractSeekOptions, + AbstractKeyIterator, + AbstractKeyIteratorOptions, + AbstractValueIterator, + AbstractValueIteratorOptions +} from './types/abstract-iterator' + +export { + AbstractChainedBatch, + AbstractChainedBatchPutOptions, + AbstractChainedBatchDelOptions, + AbstractChainedBatchWriteOptions +} from './types/abstract-chained-batch' + +export { + AbstractSublevel, + AbstractSublevelOptions +} from './types/abstract-sublevel' + +export { + NodeCallback +} from './types/interfaces' + +export * as Transcoder from 'level-transcoder' diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/index.js new file mode 100644 index 0000000..cf5dee4 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/index.js @@ -0,0 +1,8 @@ +'use strict' + +exports.AbstractLevel = require('./abstract-level').AbstractLevel +exports.AbstractSublevel = require('./abstract-level').AbstractSublevel +exports.AbstractIterator = require('./abstract-iterator').AbstractIterator +exports.AbstractKeyIterator = require('./abstract-iterator').AbstractKeyIterator +exports.AbstractValueIterator = require('./abstract-iterator').AbstractValueIterator +exports.AbstractChainedBatch = require('./abstract-chained-batch').AbstractChainedBatch diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/abstract-sublevel-iterator.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/abstract-sublevel-iterator.js new file mode 100644 index 0000000..ab6976a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/abstract-sublevel-iterator.js @@ -0,0 +1,124 @@ +'use strict' + +const { AbstractIterator, AbstractKeyIterator, AbstractValueIterator } = require('../abstract-iterator') + +const kUnfix = Symbol('unfix') +const kIterator = Symbol('iterator') +const kHandleOne = Symbol('handleOne') +const kHandleMany = Symbol('handleMany') +const kCallback = Symbol('callback') + +// TODO: unfix natively if db supports it +class AbstractSublevelIterator extends AbstractIterator { + constructor (db, options, iterator, unfix) { + super(db, options) + + this[kIterator] = iterator + this[kUnfix] = unfix + this[kHandleOne] = this[kHandleOne].bind(this) + this[kHandleMany] = this[kHandleMany].bind(this) + this[kCallback] = null + } + + [kHandleOne] (err, key, value) { + const callback = this[kCallback] + if (err) return callback(err) + if (key !== undefined) key = this[kUnfix](key) + callback(err, key, value) + } + + [kHandleMany] (err, entries) { + const callback = this[kCallback] + if (err) return callback(err) + + for (const entry of entries) { + const key = entry[0] + if (key !== undefined) entry[0] = this[kUnfix](key) + } + + callback(err, entries) + } +} + +class AbstractSublevelKeyIterator extends AbstractKeyIterator { + constructor (db, options, iterator, unfix) { + super(db, options) + + this[kIterator] = iterator + this[kUnfix] = unfix + this[kHandleOne] = this[kHandleOne].bind(this) + this[kHandleMany] = this[kHandleMany].bind(this) + this[kCallback] = null + } + + [kHandleOne] (err, key) { + const callback = this[kCallback] + if (err) return callback(err) + if (key !== undefined) key = this[kUnfix](key) + callback(err, key) + } + + [kHandleMany] (err, keys) { + const callback = this[kCallback] + if (err) return callback(err) + + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + if (key !== undefined) keys[i] = this[kUnfix](key) + } + + callback(err, keys) + } +} + +class AbstractSublevelValueIterator extends AbstractValueIterator { + constructor (db, options, iterator) { + super(db, options) + this[kIterator] = iterator + } +} + +for (const Iterator of [AbstractSublevelIterator, AbstractSublevelKeyIterator]) { + Iterator.prototype._next = function (callback) { + this[kCallback] = callback + this[kIterator].next(this[kHandleOne]) + } + + Iterator.prototype._nextv = function (size, options, callback) { + this[kCallback] = callback + this[kIterator].nextv(size, options, this[kHandleMany]) + } + + Iterator.prototype._all = function (options, callback) { + this[kCallback] = callback + this[kIterator].all(options, this[kHandleMany]) + } +} + +for (const Iterator of [AbstractSublevelValueIterator]) { + Iterator.prototype._next = function (callback) { + this[kIterator].next(callback) + } + + Iterator.prototype._nextv = function (size, options, callback) { + this[kIterator].nextv(size, options, callback) + } + + Iterator.prototype._all = function (options, callback) { + this[kIterator].all(options, callback) + } +} + +for (const Iterator of [AbstractSublevelIterator, AbstractSublevelKeyIterator, AbstractSublevelValueIterator]) { + Iterator.prototype._seek = function (target, options) { + this[kIterator].seek(target, options) + } + + Iterator.prototype._close = function (callback) { + this[kIterator].close(callback) + } +} + +exports.AbstractSublevelIterator = AbstractSublevelIterator +exports.AbstractSublevelKeyIterator = AbstractSublevelKeyIterator +exports.AbstractSublevelValueIterator = AbstractSublevelValueIterator diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/abstract-sublevel.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/abstract-sublevel.js new file mode 100644 index 0000000..aaca0fc --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/abstract-sublevel.js @@ -0,0 +1,258 @@ +'use strict' + +const ModuleError = require('module-error') +const { Buffer } = require('buffer') || {} +const { + AbstractSublevelIterator, + AbstractSublevelKeyIterator, + AbstractSublevelValueIterator +} = require('./abstract-sublevel-iterator') + +const kPrefix = Symbol('prefix') +const kUpperBound = Symbol('upperBound') +const kPrefixRange = Symbol('prefixRange') +const kParent = Symbol('parent') +const kUnfix = Symbol('unfix') + +const textEncoder = new TextEncoder() +const defaults = { separator: '!' } + +// Wrapped to avoid circular dependency +module.exports = function ({ AbstractLevel }) { + class AbstractSublevel extends AbstractLevel { + static defaults (options) { + // To help migrating from subleveldown to abstract-level + if (typeof options === 'string') { + throw new ModuleError('The subleveldown string shorthand for { separator } has been removed', { + code: 'LEVEL_LEGACY' + }) + } else if (options && options.open) { + throw new ModuleError('The subleveldown open option has been removed', { + code: 'LEVEL_LEGACY' + }) + } + + if (options == null) { + return defaults + } else if (!options.separator) { + return { ...options, separator: '!' } + } else { + return options + } + } + + // TODO: add autoClose option, which if true, does parent.attachResource(this) + constructor (db, name, options) { + // Don't forward AbstractSublevel options to AbstractLevel + const { separator, manifest, ...forward } = AbstractSublevel.defaults(options) + name = trim(name, separator) + + // Reserve one character between separator and name to give us an upper bound + const reserved = separator.charCodeAt(0) + 1 + const parent = db[kParent] || db + + // Keys should sort like ['!a!', '!a!!a!', '!a"', '!aa!', '!b!']. + // Use ASCII for consistent length between string, Buffer and Uint8Array + if (!textEncoder.encode(name).every(x => x > reserved && x < 127)) { + throw new ModuleError(`Prefix must use bytes > ${reserved} < ${127}`, { + code: 'LEVEL_INVALID_PREFIX' + }) + } + + super(mergeManifests(parent, manifest), forward) + + const prefix = (db.prefix || '') + separator + name + separator + const upperBound = prefix.slice(0, -1) + String.fromCharCode(reserved) + + this[kParent] = parent + this[kPrefix] = new MultiFormat(prefix) + this[kUpperBound] = new MultiFormat(upperBound) + this[kUnfix] = new Unfixer() + + this.nextTick = parent.nextTick + } + + prefixKey (key, keyFormat) { + if (keyFormat === 'utf8') { + return this[kPrefix].utf8 + key + } else if (key.byteLength === 0) { + // Fast path for empty key (no copy) + return this[kPrefix][keyFormat] + } else if (keyFormat === 'view') { + const view = this[kPrefix].view + const result = new Uint8Array(view.byteLength + key.byteLength) + + result.set(view, 0) + result.set(key, view.byteLength) + + return result + } else { + const buffer = this[kPrefix].buffer + return Buffer.concat([buffer, key], buffer.byteLength + key.byteLength) + } + } + + // Not exposed for now. + [kPrefixRange] (range, keyFormat) { + if (range.gte !== undefined) { + range.gte = this.prefixKey(range.gte, keyFormat) + } else if (range.gt !== undefined) { + range.gt = this.prefixKey(range.gt, keyFormat) + } else { + range.gte = this[kPrefix][keyFormat] + } + + if (range.lte !== undefined) { + range.lte = this.prefixKey(range.lte, keyFormat) + } else if (range.lt !== undefined) { + range.lt = this.prefixKey(range.lt, keyFormat) + } else { + range.lte = this[kUpperBound][keyFormat] + } + } + + get prefix () { + return this[kPrefix].utf8 + } + + get db () { + return this[kParent] + } + + _open (options, callback) { + // The parent db must open itself or be (re)opened by the user because + // a sublevel should not initiate state changes on the rest of the db. + this[kParent].open({ passive: true }, callback) + } + + _put (key, value, options, callback) { + this[kParent].put(key, value, options, callback) + } + + _get (key, options, callback) { + this[kParent].get(key, options, callback) + } + + _getMany (keys, options, callback) { + this[kParent].getMany(keys, options, callback) + } + + _del (key, options, callback) { + this[kParent].del(key, options, callback) + } + + _batch (operations, options, callback) { + this[kParent].batch(operations, options, callback) + } + + _clear (options, callback) { + // TODO (refactor): move to AbstractLevel + this[kPrefixRange](options, options.keyEncoding) + this[kParent].clear(options, callback) + } + + _iterator (options) { + // TODO (refactor): move to AbstractLevel + this[kPrefixRange](options, options.keyEncoding) + const iterator = this[kParent].iterator(options) + const unfix = this[kUnfix].get(this[kPrefix].utf8.length, options.keyEncoding) + return new AbstractSublevelIterator(this, options, iterator, unfix) + } + + _keys (options) { + this[kPrefixRange](options, options.keyEncoding) + const iterator = this[kParent].keys(options) + const unfix = this[kUnfix].get(this[kPrefix].utf8.length, options.keyEncoding) + return new AbstractSublevelKeyIterator(this, options, iterator, unfix) + } + + _values (options) { + this[kPrefixRange](options, options.keyEncoding) + const iterator = this[kParent].values(options) + return new AbstractSublevelValueIterator(this, options, iterator) + } + } + + return { AbstractSublevel } +} + +const mergeManifests = function (parent, manifest) { + return { + // Inherit manifest of parent db + ...parent.supports, + + // Disable unsupported features + createIfMissing: false, + errorIfExists: false, + + // Unset additional events because we're not forwarding them + events: {}, + + // Unset additional methods (like approximateSize) which we can't support here unless + // the AbstractSublevel class is overridden by an implementation of `abstract-level`. + additionalMethods: {}, + + // Inherit manifest of custom AbstractSublevel subclass. Such a class is not + // allowed to override encodings. + ...manifest, + + encodings: { + utf8: supportsEncoding(parent, 'utf8'), + buffer: supportsEncoding(parent, 'buffer'), + view: supportsEncoding(parent, 'view') + } + } +} + +const supportsEncoding = function (parent, encoding) { + // Prefer a non-transcoded encoding for optimal performance + return parent.supports.encodings[encoding] + ? parent.keyEncoding(encoding).name === encoding + : false +} + +class MultiFormat { + constructor (key) { + this.utf8 = key + this.view = textEncoder.encode(key) + this.buffer = Buffer ? Buffer.from(this.view.buffer, 0, this.view.byteLength) : {} + } +} + +class Unfixer { + constructor () { + this.cache = new Map() + } + + get (prefixLength, keyFormat) { + let unfix = this.cache.get(keyFormat) + + if (unfix === undefined) { + if (keyFormat === 'view') { + unfix = function (prefixLength, key) { + // Avoid Uint8Array#slice() because it copies + return key.subarray(prefixLength) + }.bind(null, prefixLength) + } else { + unfix = function (prefixLength, key) { + // Avoid Buffer#subarray() because it's slow + return key.slice(prefixLength) + }.bind(null, prefixLength) + } + + this.cache.set(keyFormat, unfix) + } + + return unfix + } +} + +const trim = function (str, char) { + let start = 0 + let end = str.length + + while (start < end && str[start] === char) start++ + while (end > start && str[end - 1] === char) end-- + + return str.slice(start, end) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/common.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/common.js new file mode 100644 index 0000000..2786f5d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/common.js @@ -0,0 +1,17 @@ +'use strict' + +exports.getCallback = function (options, callback) { + return typeof options === 'function' ? options : callback +} + +exports.getOptions = function (options, def) { + if (typeof options === 'object' && options !== null) { + return options + } + + if (def !== undefined) { + return def + } + + return {} +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/default-chained-batch.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/default-chained-batch.js new file mode 100644 index 0000000..cebb31c --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/default-chained-batch.js @@ -0,0 +1,41 @@ +'use strict' + +const { AbstractChainedBatch } = require('../abstract-chained-batch') +const ModuleError = require('module-error') +const kEncoded = Symbol('encoded') + +// Functional default for chained batch, with support of deferred open +class DefaultChainedBatch extends AbstractChainedBatch { + constructor (db) { + super(db) + this[kEncoded] = [] + } + + _put (key, value, options) { + this[kEncoded].push({ ...options, type: 'put', key, value }) + } + + _del (key, options) { + this[kEncoded].push({ ...options, type: 'del', key }) + } + + _clear () { + this[kEncoded] = [] + } + + // Assumes this[kEncoded] cannot change after write() + _write (options, callback) { + if (this.db.status === 'opening') { + this.db.defer(() => this._write(options, callback)) + } else if (this.db.status === 'open') { + if (this[kEncoded].length === 0) this.nextTick(callback) + else this.db._batch(this[kEncoded], options, callback) + } else { + this.nextTick(callback, new ModuleError('Batch is not open: cannot call write() after write() or close()', { + code: 'LEVEL_BATCH_NOT_OPEN' + })) + } + } +} + +exports.DefaultChainedBatch = DefaultChainedBatch diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/default-kv-iterator.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/default-kv-iterator.js new file mode 100644 index 0000000..7020d9e --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/default-kv-iterator.js @@ -0,0 +1,72 @@ +'use strict' + +const { AbstractKeyIterator, AbstractValueIterator } = require('../abstract-iterator') + +const kIterator = Symbol('iterator') +const kCallback = Symbol('callback') +const kHandleOne = Symbol('handleOne') +const kHandleMany = Symbol('handleMany') + +class DefaultKeyIterator extends AbstractKeyIterator { + constructor (db, options) { + super(db, options) + + this[kIterator] = db.iterator({ ...options, keys: true, values: false }) + this[kHandleOne] = this[kHandleOne].bind(this) + this[kHandleMany] = this[kHandleMany].bind(this) + } +} + +class DefaultValueIterator extends AbstractValueIterator { + constructor (db, options) { + super(db, options) + + this[kIterator] = db.iterator({ ...options, keys: false, values: true }) + this[kHandleOne] = this[kHandleOne].bind(this) + this[kHandleMany] = this[kHandleMany].bind(this) + } +} + +for (const Iterator of [DefaultKeyIterator, DefaultValueIterator]) { + const keys = Iterator === DefaultKeyIterator + const mapEntry = keys ? (entry) => entry[0] : (entry) => entry[1] + + Iterator.prototype._next = function (callback) { + this[kCallback] = callback + this[kIterator].next(this[kHandleOne]) + } + + Iterator.prototype[kHandleOne] = function (err, key, value) { + const callback = this[kCallback] + if (err) callback(err) + else callback(null, keys ? key : value) + } + + Iterator.prototype._nextv = function (size, options, callback) { + this[kCallback] = callback + this[kIterator].nextv(size, options, this[kHandleMany]) + } + + Iterator.prototype._all = function (options, callback) { + this[kCallback] = callback + this[kIterator].all(options, this[kHandleMany]) + } + + Iterator.prototype[kHandleMany] = function (err, entries) { + const callback = this[kCallback] + if (err) callback(err) + else callback(null, entries.map(mapEntry)) + } + + Iterator.prototype._seek = function (target, options) { + this[kIterator].seek(target, options) + } + + Iterator.prototype._close = function (callback) { + this[kIterator].close(callback) + } +} + +// Internal utilities, should be typed as AbstractKeyIterator and AbstractValueIterator +exports.DefaultKeyIterator = DefaultKeyIterator +exports.DefaultValueIterator = DefaultValueIterator diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/deferred-iterator.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/deferred-iterator.js new file mode 100644 index 0000000..6da54ea --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/deferred-iterator.js @@ -0,0 +1,108 @@ +'use strict' + +const { AbstractIterator, AbstractKeyIterator, AbstractValueIterator } = require('../abstract-iterator') +const ModuleError = require('module-error') + +const kNut = Symbol('nut') +const kUndefer = Symbol('undefer') +const kFactory = Symbol('factory') + +class DeferredIterator extends AbstractIterator { + constructor (db, options) { + super(db, options) + + this[kNut] = null + this[kFactory] = () => db.iterator(options) + + this.db.defer(() => this[kUndefer]()) + } +} + +class DeferredKeyIterator extends AbstractKeyIterator { + constructor (db, options) { + super(db, options) + + this[kNut] = null + this[kFactory] = () => db.keys(options) + + this.db.defer(() => this[kUndefer]()) + } +} + +class DeferredValueIterator extends AbstractValueIterator { + constructor (db, options) { + super(db, options) + + this[kNut] = null + this[kFactory] = () => db.values(options) + + this.db.defer(() => this[kUndefer]()) + } +} + +for (const Iterator of [DeferredIterator, DeferredKeyIterator, DeferredValueIterator]) { + Iterator.prototype[kUndefer] = function () { + if (this.db.status === 'open') { + this[kNut] = this[kFactory]() + } + } + + Iterator.prototype._next = function (callback) { + if (this[kNut] !== null) { + this[kNut].next(callback) + } else if (this.db.status === 'opening') { + this.db.defer(() => this._next(callback)) + } else { + this.nextTick(callback, new ModuleError('Iterator is not open: cannot call next() after close()', { + code: 'LEVEL_ITERATOR_NOT_OPEN' + })) + } + } + + Iterator.prototype._nextv = function (size, options, callback) { + if (this[kNut] !== null) { + this[kNut].nextv(size, options, callback) + } else if (this.db.status === 'opening') { + this.db.defer(() => this._nextv(size, options, callback)) + } else { + this.nextTick(callback, new ModuleError('Iterator is not open: cannot call nextv() after close()', { + code: 'LEVEL_ITERATOR_NOT_OPEN' + })) + } + } + + Iterator.prototype._all = function (options, callback) { + if (this[kNut] !== null) { + this[kNut].all(callback) + } else if (this.db.status === 'opening') { + this.db.defer(() => this._all(options, callback)) + } else { + this.nextTick(callback, new ModuleError('Iterator is not open: cannot call all() after close()', { + code: 'LEVEL_ITERATOR_NOT_OPEN' + })) + } + } + + Iterator.prototype._seek = function (target, options) { + if (this[kNut] !== null) { + // TODO: explain why we need _seek() rather than seek() here + this[kNut]._seek(target, options) + } else if (this.db.status === 'opening') { + this.db.defer(() => this._seek(target, options)) + } + } + + Iterator.prototype._close = function (callback) { + if (this[kNut] !== null) { + this[kNut].close(callback) + } else if (this.db.status === 'opening') { + this.db.defer(() => this._close(callback)) + } else { + this.nextTick(callback) + } + } +} + +exports.DeferredIterator = DeferredIterator +exports.DeferredKeyIterator = DeferredKeyIterator +exports.DeferredValueIterator = DeferredValueIterator diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/next-tick-browser.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/next-tick-browser.js new file mode 100644 index 0000000..d924b40 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/next-tick-browser.js @@ -0,0 +1,11 @@ +'use strict' + +const queueMicrotask = require('queue-microtask') + +module.exports = function (fn, ...args) { + if (args.length === 0) { + queueMicrotask(fn) + } else { + queueMicrotask(() => fn(...args)) + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/next-tick.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/next-tick.js new file mode 100644 index 0000000..cebc1fd --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/next-tick.js @@ -0,0 +1,3 @@ +'use strict' + +module.exports = process.nextTick diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/range-options.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/range-options.js new file mode 100644 index 0000000..0821ec8 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/lib/range-options.js @@ -0,0 +1,38 @@ +'use strict' + +const ModuleError = require('module-error') +const hasOwnProperty = Object.prototype.hasOwnProperty +const rangeOptions = new Set(['lt', 'lte', 'gt', 'gte']) + +module.exports = function (options, keyEncoding) { + const result = {} + + for (const k in options) { + if (!hasOwnProperty.call(options, k)) continue + if (k === 'keyEncoding' || k === 'valueEncoding') continue + + if (k === 'start' || k === 'end') { + throw new ModuleError(`The legacy range option '${k}' has been removed`, { + code: 'LEVEL_LEGACY' + }) + } else if (k === 'encoding') { + // To help migrating to abstract-level + throw new ModuleError("The levelup-style 'encoding' alias has been removed, use 'valueEncoding' instead", { + code: 'LEVEL_LEGACY' + }) + } + + if (rangeOptions.has(k)) { + // Note that we don't reject nullish and empty options here. While + // those types are invalid as keys, they are valid as range options. + result[k] = keyEncoding.encode(options[k]) + } else { + result[k] = options[k] + } + } + + result.reverse = !!result.reverse + result.limit = Number.isInteger(result.limit) && result.limit >= 0 ? result.limit : -1 + + return result +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/package.json new file mode 100644 index 0000000..7e1c587 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/package.json @@ -0,0 +1,64 @@ +{ + "name": "abstract-level", + "version": "1.0.4", + "description": "Abstract class for a lexicographically sorted key-value database", + "license": "MIT", + "main": "index.js", + "browser": { + "./lib/next-tick.js": "./lib/next-tick-browser.js" + }, + "types": "./index.d.ts", + "scripts": { + "test": "standard && ts-standard *.ts types/*.ts && hallmark && (nyc -s node test/self.js | faucet) && nyc report", + "test-browsers": "airtap --coverage test/self.js", + "coverage": "nyc report -r lcovonly" + }, + "files": [ + "abstract-chained-batch.js", + "abstract-iterator.js", + "abstract-level.js", + "index.js", + "index.d.ts", + "lib", + "test", + "types", + "CHANGELOG.md", + "UPGRADING.md" + ], + "dependencies": { + "buffer": "^6.0.3", + "catering": "^2.1.0", + "is-buffer": "^2.0.5", + "level-supports": "^4.0.0", + "level-transcoder": "^1.0.1", + "module-error": "^1.0.1", + "queue-microtask": "^1.2.3" + }, + "devDependencies": { + "@types/node": "^18.0.0", + "@voxpelli/tsconfig": "^4.0.0", + "airtap": "^4.0.4", + "airtap-playwright": "^1.0.1", + "faucet": "^0.0.3", + "hallmark": "^4.0.0", + "nyc": "^15.1.0", + "sinon": "^14.0.0", + "standard": "^16.0.4", + "tape": "^5.4.0", + "ts-standard": "^11.0.0", + "typescript": "^4.5.5" + }, + "repository": { + "type": "git", + "url": "https://github.com/Level/abstract-level.git" + }, + "homepage": "https://github.com/Level/abstract-level", + "keywords": [ + "abstract-level", + "level", + "leveldb" + ], + "engines": { + "node": ">=12" + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/async-iterator-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/async-iterator-test.js new file mode 100644 index 0000000..3f84e7d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/async-iterator-test.js @@ -0,0 +1,143 @@ +'use strict' + +const input = [{ key: '1', value: '1' }, { key: '2', value: '2' }] + +let db + +exports.setup = function (test, testCommon) { + test('setup', function (t) { + t.plan(2) + + db = testCommon.factory() + db.open(function (err) { + t.ifError(err, 'no open() error') + + db.batch(input.map(entry => ({ ...entry, type: 'put' })), function (err) { + t.ifError(err, 'no batch() error') + }) + }) + }) +} + +exports.asyncIterator = function (test, testCommon) { + for (const mode of ['iterator', 'keys', 'values']) { + test(`for await...of ${mode}()`, async function (t) { + t.plan(1) + + const it = db[mode]({ keyEncoding: 'utf8', valueEncoding: 'utf8' }) + const output = [] + + for await (const item of it) { + output.push(item) + } + + t.same(output, input.map(({ key, value }) => { + return mode === 'iterator' ? [key, value] : mode === 'keys' ? key : value + })) + }) + + testCommon.supports.permanence && test(`for await...of ${mode}() (deferred)`, async function (t) { + t.plan(1) + + const db = testCommon.factory() + await db.batch(input.map(entry => ({ ...entry, type: 'put' }))) + await db.close() + + // Don't await + db.open() + + const it = db[mode]({ keyEncoding: 'utf8', valueEncoding: 'utf8' }) + const output = [] + + for await (const item of it) { + output.push(item) + } + + t.same(output, input.map(({ key, value }) => { + return mode === 'iterator' ? [key, value] : mode === 'keys' ? key : value + })) + + await db.close() + }) + + testCommon.supports.snapshots && test(`for await...of ${mode}() (deferred, with snapshot)`, async function (t) { + t.plan(2) + + const db = testCommon.factory() + const it = db[mode]({ keyEncoding: 'utf8', valueEncoding: 'utf8' }) + const promise = db.batch(input.map(entry => ({ ...entry, type: 'put' }))) + const output = [] + + for await (const item of it) { + output.push(item) + } + + t.same(output, [], 'used snapshot') + + // Wait for data to be written + await promise + + for await (const item of db[mode]({ keyEncoding: 'utf8', valueEncoding: 'utf8' })) { + output.push(item) + } + + t.same(output, input.map(({ key, value }) => { + return mode === 'iterator' ? [key, value] : mode === 'keys' ? key : value + })) + + await db.close() + }) + + for (const deferred of [false, true]) { + test(`for await...of ${mode}() (empty, deferred: ${deferred})`, async function (t) { + const db = testCommon.factory() + const entries = [] + + if (!deferred) await db.open() + + for await (const item of db[mode]({ keyEncoding: 'utf8', valueEncoding: 'utf8' })) { + entries.push(item) + } + + t.same(entries, []) + await db.close() + }) + } + + test(`for await...of ${mode}() does not permit reuse`, async function (t) { + t.plan(3) + + const it = db[mode]() + + // eslint-disable-next-line no-unused-vars + for await (const item of it) { + t.pass('nexted') + } + + try { + // eslint-disable-next-line no-unused-vars + for await (const item of it) { + t.fail('should not be called') + } + } catch (err) { + t.is(err.code, 'LEVEL_ITERATOR_NOT_OPEN') + } + }) + } +} + +exports.teardown = function (test, testCommon) { + test('teardown', function (t) { + t.plan(1) + + db.close(function (err) { + t.ifError(err, 'no close() error') + }) + }) +} + +exports.all = function (test, testCommon) { + exports.setup(test, testCommon) + exports.asyncIterator(test, testCommon) + exports.teardown(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/batch-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/batch-test.js new file mode 100644 index 0000000..927316f --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/batch-test.js @@ -0,0 +1,320 @@ +'use strict' + +const { Buffer } = require('buffer') +const { verifyNotFoundError, assertAsync } = require('./util') +const { illegalKeys, illegalValues } = require('./util') + +let db + +exports.setUp = function (test, testCommon) { + test('setUp db', function (t) { + db = testCommon.factory() + db.open(t.end.bind(t)) + }) +} + +exports.args = function (test, testCommon) { + test('test batch() with missing `value`', assertAsync.ctx(function (t) { + t.plan(3) + + db.batch([{ type: 'put', key: 'foo1' }], assertAsync(function (err) { + t.is(err && err.code, 'LEVEL_INVALID_VALUE', 'correct error code (callback)') + })) + + db.batch([{ type: 'put', key: 'foo1' }]).catch((err) => { + t.is(err.code, 'LEVEL_INVALID_VALUE', 'correct error code (promise)') + }) + })) + + test('test batch() with illegal values', assertAsync.ctx(function (t) { + t.plan(illegalValues.length * 6) + + for (const { name, value } of illegalValues) { + db.batch([{ type: 'put', key: 'foo1', value }], assertAsync(function (err) { + t.ok(err, name + ' - has error (callback)') + t.ok(err instanceof Error, name + ' - is Error (callback)') + t.is(err && err.code, 'LEVEL_INVALID_VALUE', 'correct error code (callback)') + })) + + db.batch([{ type: 'put', key: 'foo1', value }]).catch(function (err) { + t.ok(err instanceof Error, name + ' - is Error (promise)') + t.is(err.code, 'LEVEL_INVALID_VALUE', name + ' - correct error code (promise)') + }) + } + })) + + test('test batch() with missing `key`', assertAsync.ctx(function (t) { + t.plan(3) + + db.batch([{ type: 'put', value: 'foo1' }], assertAsync(function (err) { + t.is(err && err.code, 'LEVEL_INVALID_KEY', 'correct error code (callback)') + })) + + db.batch([{ type: 'put', value: 'foo1' }]).catch(function (err) { + t.is(err.code, 'LEVEL_INVALID_KEY', 'correct error code (promise)') + }) + })) + + test('test batch() with illegal keys', assertAsync.ctx(function (t) { + t.plan(illegalKeys.length * 6) + + for (const { name, key } of illegalKeys) { + db.batch([{ type: 'put', key, value: 'foo1' }], assertAsync(function (err) { + t.ok(err, name + ' - has error (callback)') + t.ok(err instanceof Error, name + ' - is Error (callback)') + t.is(err && err.code, 'LEVEL_INVALID_KEY', 'correct error code (callback)') + })) + + db.batch([{ type: 'put', key, value: 'foo1' }]).catch(function (err) { + t.ok(err instanceof Error, name + ' - is Error (promise)') + t.is(err.code, 'LEVEL_INVALID_KEY', name + ' - correct error code (promise)') + }) + } + })) + + test('test batch() with missing or incorrect type', assertAsync.ctx(function (t) { + t.plan(10) + + db.batch([{ key: 'key', value: 'value' }], assertAsync(function (err) { + t.is(err && err.name, 'TypeError') + t.is(err && err.message, "A batch operation must have a type property that is 'put' or 'del'", 'correct error message (callback)') + })) + + db.batch([{ key: 'key', value: 'value', type: 'foo' }], assertAsync(function (err) { + t.is(err && err.name, 'TypeError') + t.is(err && err.message, "A batch operation must have a type property that is 'put' or 'del'", 'correct error message (callback)') + })) + + db.batch([{ key: 'key', value: 'value' }]).catch(function (err) { + t.is(err.name, 'TypeError') + t.is(err.message, "A batch operation must have a type property that is 'put' or 'del'", 'correct error message (promise)') + }) + + db.batch([{ key: 'key', value: 'value', type: 'foo' }]).catch(function (err) { + t.is(err.name, 'TypeError') + t.is(err.message, "A batch operation must have a type property that is 'put' or 'del'", 'correct error message (promise)') + }) + })) + + test('test batch() with missing or nullish operations', assertAsync.ctx(function (t) { + t.plan(13) + + db.batch(assertAsync(function (err) { + t.is(err && err.name, 'TypeError') + t.is(err && err.message, "The first argument 'operations' must be an array", 'correct error message (callback)') + })) + + for (const array of [null, undefined]) { + db.batch(array, assertAsync(function (err) { + t.is(err && err.name, 'TypeError') + t.is(err && err.message, "The first argument 'operations' must be an array", 'correct error message (callback)') + })) + + db.batch(array).catch(function (err) { + t.is(err.name, 'TypeError') + t.is(err.message, "The first argument 'operations' must be an array", 'correct error message (promise)') + }) + } + })) + + test('test batch() with null options', function (t) { + t.plan(2) + + db.batch([], null, function (err) { + t.error(err) + }) + + db.batch([], null).then(function () { + t.pass('resolved') + }).catch(t.fail.bind(t)) + }) + + ;[null, undefined, 1, true].forEach(function (operation) { + const type = operation === null ? 'null' : typeof operation + + test('test batch() with ' + type + ' operation', assertAsync.ctx(function (t) { + t.plan(5) + + db.batch([operation], assertAsync(function (err) { + t.is(err && err.name, 'TypeError') + t.is(err && err.message, 'A batch operation must be an object', 'correct error message (callback)') + })) + + db.batch([operation]).catch(function (err) { + t.is(err.name, 'TypeError') + t.is(err.message, 'A batch operation must be an object', 'correct error message (promise)') + }) + })) + }) + + test('test batch() with empty array', assertAsync.ctx(function (t) { + t.plan(3) + + db.batch([], assertAsync(function (err) { + t.error(err, 'no error from batch()') + })) + + db.batch([]).then(function () { + t.pass('resolved') + }).catch(t.fail.bind(t)) + })) +} + +exports.batch = function (test, testCommon) { + test('test simple batch()', function (t) { + db.batch([{ type: 'put', key: 'foo', value: 'bar' }], function (err) { + t.error(err) + + db.get('foo', function (err, value) { + t.error(err) + t.is(value, 'bar') + t.end() + }) + }) + }) + + test('test simple batch() with promise', async function (t) { + const db = testCommon.factory() + + await db.open() + await db.batch([{ type: 'put', key: 'foo', value: 'bar' }]) + + t.is(await db.get('foo', { valueEncoding: 'utf8' }), 'bar') + return db.close() + }) + + test('test multiple batch()', function (t) { + db.batch([ + { type: 'put', key: 'foobatch1', value: 'bar1' }, + { type: 'put', key: 'foobatch2', value: 'bar2' }, + { type: 'put', key: 'foobatch3', value: 'bar3' }, + { type: 'del', key: 'foobatch2' } + ], function (err) { + t.error(err) + + let r = 0 + const done = function () { + if (++r === 3) { t.end() } + } + + db.get('foobatch1', function (err, value) { + t.error(err) + t.is(value, 'bar1') + done() + }) + + db.get('foobatch2', function (err, value) { + t.ok(err, 'entry not found') + t.ok(typeof value === 'undefined', 'value is undefined') + t.ok(verifyNotFoundError(err), 'NotFound error') + done() + }) + + db.get('foobatch3', function (err, value) { + t.error(err) + t.is(value, 'bar3') + done() + }) + }) + }) + + for (const encoding of ['utf8', 'buffer', 'view']) { + if (!testCommon.supports.encodings[encoding]) continue + + // NOTE: adapted from memdown + test(`empty values in batch with ${encoding} valueEncoding`, async function (t) { + const db = testCommon.factory({ valueEncoding: encoding }) + const values = ['', Uint8Array.from([]), Buffer.alloc(0)] + const expected = encoding === 'utf8' ? values[0] : encoding === 'view' ? values[1] : values[2] + + await db.open() + await db.batch(values.map((value, i) => ({ type: 'put', key: String(i), value }))) + + for (let i = 0; i < values.length; i++) { + const value = await db.get(String(i)) + + // Buffer is a Uint8Array, so this is allowed + if (encoding === 'view' && Buffer.isBuffer(value)) { + t.same(value, values[2]) + } else { + t.same(value, expected) + } + } + + return db.close() + }) + + test(`empty keys in batch with ${encoding} keyEncoding`, async function (t) { + const db = testCommon.factory({ keyEncoding: encoding }) + const keys = ['', Uint8Array.from([]), Buffer.alloc(0)] + + await db.open() + + for (let i = 0; i < keys.length; i++) { + await db.batch([{ type: 'put', key: keys[i], value: String(i) }]) + t.same(await db.get(keys[i]), String(i), `got value ${i}`) + } + + return db.close() + }) + } +} + +exports.atomic = function (test, testCommon) { + test('test batch() is atomic', function (t) { + t.plan(4) + + let async = false + + db.batch([ + { type: 'put', key: 'foobah1', value: 'bar1' }, + { type: 'put', value: 'bar2' }, + { type: 'put', key: 'foobah3', value: 'bar3' } + ], function (err) { + t.ok(err, 'should error') + t.ok(async, 'callback is asynchronous') + + db.get('foobah1', function (err) { + t.ok(err, 'should not be found') + }) + db.get('foobah3', function (err) { + t.ok(err, 'should not be found') + }) + }) + + async = true + }) +} + +exports.events = function (test, testCommon) { + test('test batch([]) (array-form) emits batch event', async function (t) { + t.plan(2) + + const db = testCommon.factory() + await db.open() + + t.ok(db.supports.events.batch) + + db.on('batch', function (ops) { + t.same(ops, [{ type: 'put', key: 456, value: 99, custom: 123 }]) + }) + + await db.batch([{ type: 'put', key: 456, value: 99, custom: 123 }]) + await db.close() + }) +} + +exports.tearDown = function (test, testCommon) { + test('tearDown', function (t) { + db.close(t.end.bind(t)) + }) +} + +exports.all = function (test, testCommon) { + exports.setUp(test, testCommon) + exports.args(test, testCommon) + exports.batch(test, testCommon) + exports.atomic(test, testCommon) + exports.events(test, testCommon) + exports.tearDown(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/chained-batch-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/chained-batch-test.js new file mode 100644 index 0000000..9e217a7 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/chained-batch-test.js @@ -0,0 +1,295 @@ +'use strict' + +let db + +exports.setUp = function (test, testCommon) { + test('setUp db', function (t) { + db = testCommon.factory() + db.open(t.end.bind(t)) + }) +} + +exports.args = function (test, testCommon) { + test('test batch has db reference', function (t) { + t.ok(db.batch().db === db) + t.end() + }) + + test('test batch#put() with missing, null or undefined `value`', function (t) { + t.plan(3 * 2) + + for (const args of [[null], [undefined], []]) { + const batch = db.batch() + + try { + batch.put('key', ...args) + } catch (err) { + t.is(err.code, 'LEVEL_INVALID_VALUE', 'correct error code') + t.is(batch.length, 0, 'length is not incremented on error') + } + } + }) + + test('test batch#put() with missing, null or undefined `key`', function (t) { + t.plan(3 * 2) + + for (const args of [[], [null, 'foo'], [undefined, 'foo']]) { + const batch = db.batch() + + try { + batch.put(...args) + } catch (err) { + t.is(err.code, 'LEVEL_INVALID_KEY', 'correct error code') + t.is(batch.length, 0, 'length is not incremented on error') + } + } + }) + + test('test batch#del() with missing, null or undefined `key`', function (t) { + t.plan(3 * 2) + + for (const args of [[null], [undefined], []]) { + const batch = db.batch() + + try { + batch.del(...args) + } catch (err) { + t.is(err.code, 'LEVEL_INVALID_KEY', 'correct error code') + t.is(batch.length, 0, 'length is not incremented on error') + } + } + }) + + test('test batch#clear() doesn\'t throw', function (t) { + db.batch().clear() + t.end() + }) + + test('test batch#put() after write()', function (t) { + const batch = db.batch().put('foo', 'bar') + batch.write(function () {}) + try { + batch.put('boom', 'bang') + } catch (err) { + t.is(err.code, 'LEVEL_BATCH_NOT_OPEN', 'correct error code') + return t.end() + } + t.fail('should have thrown') + t.end() + }) + + test('test batch#del() after write()', function (t) { + const batch = db.batch().put('foo', 'bar') + batch.write(function () {}) + try { + batch.del('foo') + } catch (err) { + t.is(err.code, 'LEVEL_BATCH_NOT_OPEN', 'correct error code') + return t.end() + } + t.fail('should have thrown') + t.end() + }) + + test('test batch#clear() after write()', function (t) { + const batch = db.batch().put('foo', 'bar') + batch.write(function () {}) + try { + batch.clear() + } catch (err) { + t.is(err.code, 'LEVEL_BATCH_NOT_OPEN', 'correct error code') + return t.end() + } + t.fail('should have thrown') + t.end() + }) + + test('test batch#write() after write()', function (t) { + t.plan(1) + const batch = db.batch().put('foo', 'bar') + batch.write(function () {}) + batch.write(function (err) { + t.is(err && err.code, 'LEVEL_BATCH_NOT_OPEN', 'correct error code') + }) + }) + + test('test batch#write() with no operations', function (t) { + let async = false + + db.batch().write(function (err) { + t.ifError(err, 'no error from write()') + t.ok(async, 'callback is asynchronous') + t.end() + }) + + async = true + }) + + test('test batch#write() with promise and no operations', function (t) { + db.batch().write() + .then(t.end.bind(t)) + .catch(t.end.bind(t)) + }) + + test('test twice batch#close() is idempotent', function (t) { + const batch = db.batch() + batch.close(function () { + let async = false + + batch.close(function () { + t.ok(async, 'callback is asynchronous') + t.end() + }) + + async = true + }) + }) +} + +exports.batch = function (test, testCommon) { + test('test basic batch', function (t) { + db.batch([ + { type: 'put', key: 'one', value: '1' }, + { type: 'put', key: 'two', value: '2' }, + { type: 'put', key: 'three', value: '3' } + ], function (err) { + t.error(err) + + const batch = db.batch() + .put('1', 'one') + .del('2', 'two') + .put('3', 'three') + + t.is(batch.length, 3, 'length was incremented') + + batch.clear() + t.is(batch.length, 0, 'length is reset') + + batch.put('one', 'I') + .put('two', 'II') + .del('three') + .put('foo', 'bar') + + t.is(batch.length, 4, 'length was incremented') + + batch.write(function (err) { + t.error(err, 'no write() error') + + db.iterator({ keyEncoding: 'utf8', valueEncoding: 'utf8' }).all(function (err, entries) { + t.error(err) + t.same(entries, [ + ['foo', 'bar'], + ['one', 'I'], + ['two', 'II'] + ]) + t.end() + }) + }) + }) + }) + + test('test basic batch with promise', function (t) { + const db = testCommon.factory() + + db.open(function (err) { + t.error(err) + + db.batch() + .put('1', 'one') + .put('2', 'two') + .put('3', 'three') + .write().then(function () { + db.iterator({ keyEncoding: 'utf8', valueEncoding: 'utf8' }).all(function (err, entries) { + t.error(err) + t.same(entries, [ + ['1', 'one'], + ['2', 'two'], + ['3', 'three'] + ]) + db.close(t.end.bind(t)) + }) + }).catch(t.fail.bind(t)) + }) + }) + + // NOTE: adapted from levelup + test('chained batch with per-operation encoding options', async function (t) { + t.plan(2) + + const db = testCommon.factory() + await db.open() + + db.once('batch', function (operations) { + t.same(operations, [ + { type: 'put', key: 'a', value: 'a', valueEncoding: 'json' }, + { type: 'put', key: 'b', value: 'b' }, + { type: 'put', key: '"c"', value: 'c' }, + { type: 'del', key: 'c', keyEncoding: 'json', arbitraryOption: true } + ]) + }) + + await db.batch() + .put('a', 'a', { valueEncoding: 'json' }) + .put('b', 'b') + .put('"c"', 'c') + .del('c', { keyEncoding: 'json', arbitraryOption: true }) + .write() + + t.same(await db.iterator().all(), [ + ['a', '"a"'], + ['b', 'b'] + ]) + + return db.close() + }) +} + +exports.events = function (test, testCommon) { + test('test chained batch() emits batch event', async function (t) { + t.plan(2) + + const db = testCommon.factory() + await db.open() + + t.ok(db.supports.events.batch) + + db.on('batch', function (ops) { + t.same(ops, [ + { type: 'put', key: 987, value: 'b', custom: 123 }, + { type: 'del', key: 216, custom: 999 } + ]) + }) + + await db.batch().put(987, 'b', { custom: 123 }).del(216, { custom: 999 }).write() + await db.close() + }) + + test('test close() on chained batch event', async function () { + const db = testCommon.factory() + await db.open() + + let promise + + db.on('batch', function () { + // Should not interfere with the current write() operation + promise = db.close() + }) + + await db.batch().put('a', 'b').write() + await promise + }) +} + +exports.tearDown = function (test, testCommon) { + test('tearDown', function (t) { + db.close(t.end.bind(t)) + }) +} + +exports.all = function (test, testCommon) { + exports.setUp(test, testCommon) + exports.args(test, testCommon) + exports.batch(test, testCommon) + exports.events(test, testCommon) + exports.tearDown(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/clear-range-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/clear-range-test.js new file mode 100644 index 0000000..850b9c8 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/clear-range-test.js @@ -0,0 +1,274 @@ +'use strict' + +const data = (function () { + const d = [] + let i = 0 + let k + for (; i < 100; i++) { + k = (i < 10 ? '0' : '') + i + d.push({ + key: k, + value: String(Math.random()) + }) + } + return d +}()) + +exports.range = function (test, testCommon) { + function rangeTest (name, opts, expected) { + test('db#clear() with ' + name, function (t) { + prepare(t, function (db) { + db.clear(opts, function (err) { + t.ifError(err, 'no clear error') + verify(t, db, expected) + }) + }) + }) + } + + function prepare (t, callback) { + const db = testCommon.factory() + + db.open(function (err) { + t.ifError(err, 'no open error') + + db.batch(data.map(function (d) { + return { + type: 'put', + key: d.key, + value: d.value + } + }), function (err) { + t.ifError(err, 'no batch error') + callback(db) + }) + }) + } + + function verify (t, db, expected) { + const it = db.iterator({ keyEncoding: 'utf8', valueEncoding: 'utf8' }) + + it.all(function (err, entries) { + t.ifError(err, 'no all() error') + t.is(entries.length, expected.length, 'correct number of entries') + t.same(entries, expected.map(kv => [kv.key, kv.value])) + + db.close(t.end.bind(t)) + }) + } + + function exclude (data, start, end, expectedLength) { + data = data.slice() + const removed = data.splice(start, end - start + 1) // Inclusive + if (expectedLength != null) checkLength(removed, expectedLength) + return data + } + + // For sanity checks on test arguments + function checkLength (arr, length) { + if (arr.length !== length) { + throw new RangeError('Expected ' + length + ' elements, got ' + arr.length) + } + + return arr + } + + rangeTest('full range', {}, []) + + // Reversing has no effect without limit + rangeTest('reverse=true', { + reverse: true + }, []) + + rangeTest('gte=00', { + gte: '00' + }, []) + + rangeTest('gte=50', { + gte: '50' + }, data.slice(0, 50)) + + rangeTest('lte=50 and reverse=true', { + lte: '50', + reverse: true + }, data.slice(51)) + + rangeTest('gte=49.5 (midway)', { + gte: '49.5' + }, data.slice(0, 50)) + + rangeTest('gte=49999 (midway)', { + gte: '49999' + }, data.slice(0, 50)) + + rangeTest('lte=49.5 (midway) and reverse=true', { + lte: '49.5', + reverse: true + }, data.slice(50)) + + rangeTest('lt=49.5 (midway) and reverse=true', { + lt: '49.5', + reverse: true + }, data.slice(50)) + + rangeTest('lt=50 and reverse=true', { + lt: '50', + reverse: true + }, data.slice(50)) + + rangeTest('lte=50', { + lte: '50' + }, data.slice(51)) + + rangeTest('lte=50.5 (midway)', { + lte: '50.5' + }, data.slice(51)) + + rangeTest('lte=50555 (midway)', { + lte: '50555' + }, data.slice(51)) + + rangeTest('lt=50555 (midway)', { + lt: '50555' + }, data.slice(51)) + + rangeTest('gte=50.5 (midway) and reverse=true', { + gte: '50.5', + reverse: true + }, data.slice(0, 51)) + + rangeTest('gt=50.5 (midway) and reverse=true', { + gt: '50.5', + reverse: true + }, data.slice(0, 51)) + + rangeTest('gt=50 and reverse=true', { + gt: '50', + reverse: true + }, data.slice(0, 51)) + + // First key is actually '00' so it should avoid it + rangeTest('lte=0', { + lte: '0' + }, data) + + // First key is actually '00' so it should avoid it + rangeTest('lt=0', { + lt: '0' + }, data) + + rangeTest('gte=30 and lte=70', { + gte: '30', + lte: '70' + }, exclude(data, 30, 70)) + + // The gte and lte options should take precedence over gt and lt respectively. + rangeTest('test iterator with gte=30 and lte=70 and gt=40 and lt=60', { + gte: '30', + lte: '70', + gt: '40', + lt: '60' + }, exclude(data, 30, 70)) + + // Also test the other way around: if gt and lt were to select a bigger range. + rangeTest('test iterator with gte=30 and lte=70 and gt=20 and lt=80', { + gte: '30', + lte: '70', + gt: '20', + lt: '80' + }, exclude(data, 30, 70)) + + rangeTest('gt=29 and lt=71', { + gt: '29', + lt: '71' + }, exclude(data, 30, 70)) + + rangeTest('gte=30 and lte=70 and reverse=true', { + lte: '70', + gte: '30', + reverse: true + }, exclude(data, 30, 70)) + + rangeTest('gt=29 and lt=71 and reverse=true', { + lt: '71', + gt: '29', + reverse: true + }, exclude(data, 30, 70)) + + rangeTest('limit=20', { + limit: 20 + }, data.slice(20)) + + rangeTest('limit=20 and gte=20', { + limit: 20, + gte: '20' + }, exclude(data, 20, 39, 20)) + + rangeTest('limit=20 and reverse=true', { + limit: 20, + reverse: true + }, data.slice(0, -20)) + + rangeTest('limit=20 and lte=79 and reverse=true', { + limit: 20, + lte: '79', + reverse: true + }, exclude(data, 60, 79, 20)) + + rangeTest('limit=-1 should clear whole database', { + limit: -1 + }, []) + + rangeTest('limit=0 should not clear anything', { + limit: 0 + }, data) + + rangeTest('lte after limit', { + limit: 20, + lte: '50' + }, data.slice(20)) + + rangeTest('lte before limit', { + limit: 50, + lte: '19' + }, data.slice(20)) + + rangeTest('gte after database end', { + gte: '9a' + }, data) + + rangeTest('gt after database end', { + gt: '9a' + }, data) + + rangeTest('lte after database end and reverse=true', { + lte: '9a', + reverse: true + }, []) + + rangeTest('lte and gte after database and reverse=true', { + lte: '9b', + gte: '9a', + reverse: true + }, data) + + rangeTest('lt and gt after database and reverse=true', { + lt: '9b', + gt: '9a', + reverse: true + }, data) + + rangeTest('gt greater than lt', { + gt: '20', + lt: '10' + }, data) + + rangeTest('gte greater than lte', { + gte: '20', + lte: '10' + }, data) +} + +exports.all = function (test, testCommon) { + exports.range(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/clear-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/clear-test.js new file mode 100644 index 0000000..04b76a6 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/clear-test.js @@ -0,0 +1,183 @@ +'use strict' + +const isBuffer = require('is-buffer') +const { Buffer } = require('buffer') + +exports.args = function (test, testCommon) { + test('test clear() with legacy range options', function (t) { + t.plan(4) + + const db = testCommon.factory() + + db.open(function (err) { + t.ifError(err) + + try { + db.clear({ start: 'foo' }, t.fail.bind(t)) + } catch (err) { + t.is(err.code, 'LEVEL_LEGACY') + } + + try { + db.clear({ end: 'foo' }).catch(t.fail.bind(t)) + } catch (err) { + t.is(err.code, 'LEVEL_LEGACY') + } + + db.close(t.ifError.bind(t)) + }) + }) +} + +exports.clear = function (test, testCommon) { + makeTest('string', ['a', 'b']) + + if (testCommon.supports.encodings.buffer) { + makeTest('buffer', [Buffer.from('a'), Buffer.from('b')]) + makeTest('mixed', [Buffer.from('a'), 'b']) + + // These keys would be equal when compared as utf8 strings + makeTest('non-utf8 buffer', [Buffer.from('80', 'hex'), Buffer.from('c0', 'hex')]) + } + + function makeTest (type, keys) { + test('test simple clear() on ' + type + ' keys', function (t) { + t.plan(8) + + const db = testCommon.factory() + const ops = keys.map(function (key) { + return { type: 'put', key: key, value: 'foo', keyEncoding: isBuffer(key) ? 'buffer' : 'utf8' } + }) + + db.open(function (err) { + t.ifError(err, 'no open error') + + db.batch(ops, function (err) { + t.ifError(err, 'no batch error') + + db.iterator().all(function (err, entries) { + t.ifError(err, 'no all() error') + t.is(entries.length, keys.length, 'has entries') + + db.clear(function (err) { + t.ifError(err, 'no clear error') + + db.iterator().all(function (err, entries) { + t.ifError(err, 'no all() error') + t.is(entries.length, 0, 'has no entries') + + db.close(function (err) { + t.ifError(err, 'no close error') + }) + }) + }) + }) + }) + }) + }) + + test('test simple clear() on ' + type + ' keys, with promise', function (t) { + t.plan(8) + + const db = testCommon.factory() + const ops = keys.map(function (key) { + return { type: 'put', key: key, value: 'foo', keyEncoding: isBuffer(key) ? 'buffer' : 'utf8' } + }) + + db.open(function (err) { + t.ifError(err, 'no open error') + + db.batch(ops, function (err) { + t.ifError(err, 'no batch error') + + db.iterator().all(function (err, entries) { + t.ifError(err, 'no all() error') + t.is(entries.length, keys.length, 'has entries') + + db.clear().then(function () { + t.ifError(err, 'no clear error') + + db.iterator().all(function (err, entries) { + t.ifError(err, 'no all() error') + t.is(entries.length, 0, 'has no entries') + + db.close(function (err) { + t.ifError(err, 'no close error') + }) + }) + }).catch(t.fail.bind(t)) + }) + }) + }) + }) + } + + // NOTE: adapted from levelup + for (const deferred of [false, true]) { + for (const [gte, keyEncoding] of [['"b"', 'utf8'], ['b', 'json']]) { + test(`clear() with ${keyEncoding} encoding (deferred: ${deferred})`, async function (t) { + const db = testCommon.factory() + + await db.open() + await db.batch([ + { type: 'put', key: '"a"', value: 'a' }, + { type: 'put', key: '"b"', value: 'b' } + ]) + + if (deferred) { + await db.close() + t.is(db.status, 'closed') + db.open(t.ifError.bind(t)) + t.is(db.status, 'opening') + } + + await db.clear({ gte, keyEncoding }) + + const keys = await db.keys().all() + t.same(keys, ['"a"'], 'got expected keys') + + return db.close() + }) + } + } +} + +exports.events = function (test, testCommon) { + test('test clear() with options emits clear event', async function (t) { + t.plan(2) + + const db = testCommon.factory() + await db.open() + + t.ok(db.supports.events.clear) + + db.on('clear', function (options) { + t.same(options, { gt: 567, custom: 123 }) + }) + + await db.clear({ gt: 567, custom: 123 }) + await db.close() + }) + + test('test clear() without options emits clear event', async function (t) { + t.plan(2) + + const db = testCommon.factory() + await db.open() + + t.ok(db.supports.events.clear) + + db.on('clear', function (options) { + t.same(options, {}) + }) + + await db.clear() + await db.close() + }) +} + +exports.all = function (test, testCommon) { + exports.args(test, testCommon) + exports.events(test, testCommon) + exports.clear(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/close-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/close-test.js new file mode 100644 index 0000000..d0b77c4 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/close-test.js @@ -0,0 +1,32 @@ +'use strict' + +exports.close = function (test, testCommon) { + test('test close()', function (t) { + const db = testCommon.factory() + + db.open(function (err) { + t.ifError(err, 'no open() error') + + db.close(function (err) { + t.error(err) + t.end() + }) + }) + }) + + test('test close() with promise', function (t) { + const db = testCommon.factory() + + db.open(function (err) { + t.ifError(err, 'no open() error') + + db.close() + .then(t.end.bind(t)) + .catch(t.end.bind(t)) + }) + }) +} + +exports.all = function (test, testCommon) { + exports.close(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/common.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/common.js new file mode 100644 index 0000000..c034d91 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/common.js @@ -0,0 +1,90 @@ +'use strict' + +const kNone = Symbol('none') +const kProtected = Symbol('protected') + +function testCommon (options) { + const factory = options.factory + const test = options.test + + if (typeof factory !== 'function') { + throw new TypeError('factory must be a function') + } + + if (typeof test !== 'function') { + throw new TypeError('test must be a function') + } + + if (options.legacyRange != null) { + throw new Error('The legacyRange option has been removed') + } + + let supports = kNone + + return protect(options, { + test: test, + factory: factory, + internals: options.internals || {}, + + // Expose manifest through testCommon to more easily skip tests based on + // supported features. Use a getter to only create a db once. Implicitly + // we also test that the manifest doesn't change after the db constructor. + get supports () { + if (supports === kNone) this.supports = this.factory().supports + return supports + }, + + // Prefer assigning early via manifest-test unless test.only() is used + // in which case we create the manifest on-demand. Copy it to be safe. + set supports (value) { + if (supports === kNone) supports = JSON.parse(JSON.stringify(value)) + } + }) +} + +module.exports = testCommon + +// To help migrating from abstract-leveldown. +// Throw if test suite options are used instead of db.supports +function protect (options, testCommon) { + const legacyOptions = [ + ['createIfMissing', true], + ['errorIfExists', true], + ['snapshots', true], + ['seek', true], + ['encodings', true], + ['deferredOpen', true], + ['streams', true], + ['clear', true], + ['getMany', true], + ['bufferKeys', false], + ['serialize', false], + ['idempotentOpen', false], + ['passiveOpen', false], + ['openCallback', false] + ] + + Object.defineProperty(testCommon, kProtected, { + value: true + }) + + for (const [k, exists] of legacyOptions) { + const msg = exists ? 'has moved to db.supports' : 'has been removed' + + // Options may be a testCommon instance + if (!options[kProtected] && k in options) { + throw new Error(`The test suite option '${k}' ${msg}`) + } + + Object.defineProperty(testCommon, k, { + get () { + throw new Error(`The test suite option '${k}' ${msg}`) + }, + set () { + throw new Error(`The test suite option '${k}' ${msg}`) + } + }) + } + + return testCommon +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/deferred-open-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/deferred-open-test.js new file mode 100644 index 0000000..2129173 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/deferred-open-test.js @@ -0,0 +1,329 @@ +'use strict' + +const { DeferredIterator } = require('../lib/deferred-iterator') + +exports.all = function (test, testCommon) { + function verifyValues (t, db, entries) { + let pendingGets = 3 + + for (let k = 1; k <= entries; k++) { + db.get('k' + k, { valueEncoding: 'utf8' }, function (err, v) { + t.ifError(err, 'no get() error') + t.is(v, 'v' + k, 'value is ok') + t.is(db.status, 'open', 'status is ok') + + if (--pendingGets <= 0) { + db.get('k4', { valueEncoding: 'utf8' }, function (err) { + t.ok(err) + db.close(t.ifError.bind(t)) + }) + } + }) + } + } + + // NOTE: copied from levelup + test('deferred open(): put() and get() on new database', function (t) { + t.plan(15) + + // Open database without callback, opens in next tick + const db = testCommon.factory() + + let pendingPuts = 3 + + // Insert 3 values with put(), these should be deferred until the database is actually open + for (let k = 1; k <= 3; k++) { + db.put('k' + k, 'v' + k, function (err) { + t.ifError(err, 'no put() error') + + if (--pendingPuts <= 0) { + verifyValues(t, db, 3) + } + }) + } + + t.is(db.status, 'opening') + }) + + // NOTE: copied from levelup + test('deferred open(): batch() on new database', function (t) { + t.plan(13) + + // Open database without callback, opens in next tick + const db = testCommon.factory() + + // Insert 3 values with batch(), these should be deferred until the database is actually open + db.batch([ + { type: 'put', key: 'k1', value: 'v1' }, + { type: 'put', key: 'k2', value: 'v2' }, + { type: 'put', key: 'k3', value: 'v3' } + ], function (err) { + t.ifError(err, 'no batch() error') + verifyValues(t, db, 3) + }) + + t.is(db.status, 'opening') + }) + + // NOTE: copied from levelup + test('deferred open(): chained batch() on new database', function (t) { + t.plan(13) + + // Open database without callback, opens in next tick + const db = testCommon.factory() + + // Insert 3 values with batch(), these should be deferred until the database is actually open + db.batch() + .put('k1', 'v1') + .put('k2', 'v2') + .put('k3', 'v3') + .write(function (err) { + t.ifError(err, 'no write() error') + verifyValues(t, db, 3) + }) + + t.is(db.status, 'opening') + }) + + // NOTE: copied from levelup + test('deferred open(): put() and get() on reopened database', async function (t) { + const db = testCommon.factory() + + await db.close() + t.is(db.status, 'closed') + + db.open(() => {}) + t.is(db.status, 'opening') + + await db.put('beep', 'boop') + + t.is(db.status, 'open') + t.is(await db.get('beep', { valueEncoding: 'utf8' }), 'boop') + + await db.close() + }) + + // NOTE: copied from levelup + test('deferred open(): value of queued operation is not stringified', function (t) { + t.plan(4) + + const db = testCommon.factory({ valueEncoding: 'json' }) + + db.put('key', { thing: 2 }, function (err) { + t.ifError(err) + + db.get('key', function (err, value) { + t.ifError(err) + t.same(value, { thing: 2 }) + db.close(t.ifError.bind(t)) + }) + }) + }) + + // NOTE: copied from levelup + test('deferred open(): key of queued operation is not stringified', function (t) { + t.plan(4) + + const db = testCommon.factory({ keyEncoding: 'json' }) + + db.put({ thing: 2 }, 'value', function (err) { + t.ifError(err) + + db.iterator().next(function (err, key, value) { + t.ifError(err, 'no next() error') + t.same(key, { thing: 2 }) + db.close(t.ifError.bind(t)) + }) + }) + }) + + // NOTE: copied from deferred-leveldown + test('cannot operate on closed db', function (t) { + t.plan(6) + + const db = testCommon.factory() + + db.open(function (err) { + t.ifError(err) + + db.close(function (err) { + t.ifError(err) + + db.put('foo', 'bar', function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + }) + + try { + db.iterator() + } catch (err) { + t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN') + } + + try { + db.keys() + } catch (err) { + t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN') + } + + try { + db.values() + } catch (err) { + t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN') + } + }) + }) + }) + + // NOTE: copied from deferred-leveldown + test('cannot operate on closing db', function (t) { + t.plan(6) + + const db = testCommon.factory() + + db.open(function (err) { + t.ifError(err) + + db.close(function (err) { + t.ifError(err) + }) + + db.put('foo', 'bar', function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + }) + + try { + db.iterator() + } catch (err) { + t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN') + } + + try { + db.keys() + } catch (err) { + t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN') + } + + try { + db.values() + } catch (err) { + t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN') + } + }) + }) + + // NOTE: copied from deferred-leveldown + test('deferred iterator - cannot operate on closed db', function (t) { + t.plan(10) + + const db = testCommon.factory() + + db.open(function (err) { + t.error(err, 'no error') + + db.close(function (err) { + t.ifError(err) + + it.next(function (err, key, value) { + t.is(err && err.code, 'LEVEL_ITERATOR_NOT_OPEN') + }) + + it.next().catch(function (err) { + t.is(err.code, 'LEVEL_ITERATOR_NOT_OPEN') + }) + + it.nextv(10, function (err, items) { + t.is(err && err.code, 'LEVEL_ITERATOR_NOT_OPEN') + }) + + it.nextv(10).catch(function (err) { + t.is(err.code, 'LEVEL_ITERATOR_NOT_OPEN') + }) + + it.all(function (err, items) { + t.is(err && err.code, 'LEVEL_ITERATOR_NOT_OPEN') + }) + + it.all().catch(function (err) { + t.is(err.code, 'LEVEL_ITERATOR_NOT_OPEN') + }) + + // Was already closed + it.close(function () { + t.ifError(err, 'no close() error') + }) + + it.close().catch(function () { + t.fail('no close() error') + }) + + try { + it.seek('foo') + } catch (err) { + // Should *not* throw + t.fail(err) + } + }) + }) + + const it = db.iterator({ gt: 'foo' }) + t.ok(it instanceof DeferredIterator) + }) + + // NOTE: copied from deferred-leveldown + test('deferred iterator - cannot operate on closing db', function (t) { + t.plan(10) + + const db = testCommon.factory() + + db.open(function (err) { + t.error(err, 'no error') + + db.close(function (err) { + t.ifError(err) + }) + + it.next(function (err, key, value) { + t.is(err && err.code, 'LEVEL_ITERATOR_NOT_OPEN') + }) + + it.next().catch(function (err) { + t.is(err.code, 'LEVEL_ITERATOR_NOT_OPEN') + }) + + it.nextv(10, function (err) { + t.is(err && err.code, 'LEVEL_ITERATOR_NOT_OPEN') + }) + + it.nextv(10).catch(function (err) { + t.is(err.code, 'LEVEL_ITERATOR_NOT_OPEN') + }) + + it.all(function (err) { + t.is(err && err.code, 'LEVEL_ITERATOR_NOT_OPEN') + }) + + it.all().catch(function (err) { + t.is(err.code, 'LEVEL_ITERATOR_NOT_OPEN') + }) + + // Is already closing + it.close(function (err) { + t.ifError(err, 'no close() error') + }) + + it.close().catch(function () { + t.fail('no close() error') + }) + + try { + it.seek('foo') + } catch (err) { + // Should *not* throw + t.fail(err) + } + }) + + const it = db.iterator({ gt: 'foo' }) + t.ok(it instanceof DeferredIterator) + }) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/del-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/del-test.js new file mode 100644 index 0000000..bab9b0d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/del-test.js @@ -0,0 +1,105 @@ +'use strict' + +const { verifyNotFoundError, illegalKeys, assertAsync } = require('./util') + +let db + +exports.setUp = function (test, testCommon) { + test('setUp db', function (t) { + db = testCommon.factory() + db.open(t.end.bind(t)) + }) +} + +exports.args = function (test, testCommon) { + test('test del() with illegal keys', assertAsync.ctx(function (t) { + t.plan(illegalKeys.length * 5) + + for (const { name, key } of illegalKeys) { + db.del(key, assertAsync(function (err) { + t.ok(err instanceof Error, name + ' - is Error (callback)') + t.is(err && err.code, 'LEVEL_INVALID_KEY', name + ' - correct error code (callback)') + })) + + db.del(key).catch(function (err) { + t.ok(err instanceof Error, name + ' - is Error (promise)') + t.is(err.code, 'LEVEL_INVALID_KEY', name + ' - correct error code (callback)') + }) + } + })) +} + +exports.del = function (test, testCommon) { + test('test simple del()', function (t) { + db.put('foo', 'bar', function (err) { + t.error(err) + db.del('foo', function (err) { + t.error(err) + db.get('foo', function (err, value) { + t.ok(err, 'entry properly deleted') + t.ok(typeof value === 'undefined', 'value is undefined') + t.ok(verifyNotFoundError(err), 'NotFound error') + t.end() + }) + }) + }) + }) + + test('test simple del() with promise', function (t) { + db.put('foo', 'bar', function (err) { + t.error(err) + db.del('foo').then(function (err) { + t.error(err) + db.get('foo', function (err, value) { + t.ok(err, 'entry properly deleted') + t.ok(typeof value === 'undefined', 'value is undefined') + t.ok(verifyNotFoundError(err), 'NotFound error') + t.end() + }) + }).catch(t.fail.bind(t)) + }) + }) + + test('test del on non-existent key', function (t) { + db.del('blargh', function (err) { + t.error(err) + t.end() + }) + }) + + test('test del on non-existent key, with promise', async function (t) { + return db.del('blargh') + }) +} + +exports.events = function (test, testCommon) { + test('test del() emits del event', async function (t) { + t.plan(2) + + const db = testCommon.factory() + await db.open() + + t.ok(db.supports.events.del) + + db.on('del', function (key) { + t.is(key, 456) + }) + + await db.del(456) + await db.close() + }) +} + +exports.tearDown = function (test, testCommon) { + test('tearDown', function (t) { + db.close(t.end.bind(t)) + }) +} + +exports.all = function (test, testCommon) { + exports.setUp(test, testCommon) + exports.args(test, testCommon) + exports.del(test, testCommon) + exports.events(test, testCommon) + exports.tearDown(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-buffer-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-buffer-test.js new file mode 100644 index 0000000..6535422 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-buffer-test.js @@ -0,0 +1,255 @@ +'use strict' + +const { Buffer } = require('buffer') +const textEncoder = new TextEncoder() + +exports.all = function (test, testCommon) { + if (!testCommon.supports.encodings.buffer) return + + // NOTE: adapted from levelup + test('test put() and get() with buffer value and buffer valueEncoding', async function (t) { + const db = testCommon.factory() + await db.open() + await db.put('test', testBuffer(), { valueEncoding: 'buffer' }) + t.same(await db.get('test', { valueEncoding: 'buffer' }), testBuffer()) + return db.close() + }) + + // NOTE: adapted from levelup + test('test put() and get() with buffer value and buffer valueEncoding in factory', async function (t) { + const db = testCommon.factory({ valueEncoding: 'buffer' }) + await db.open() + await db.put('test', testBuffer()) + t.same(await db.get('test'), testBuffer()) + return db.close() + }) + + // NOTE: adapted from levelup + test('test put() and get() with buffer key and buffer keyEncoding', async function (t) { + const db = testCommon.factory() + await db.open() + await db.put(testBuffer(), 'test', { keyEncoding: 'buffer' }) + t.same(await db.get(testBuffer(), { keyEncoding: 'buffer' }), 'test') + return db.close() + }) + + // NOTE: adapted from levelup + test('test put() and get() with buffer key and utf8 keyEncoding', async function (t) { + const db = testCommon.factory() + await db.open() + await db.put(Buffer.from('foo🐄'), 'test', { keyEncoding: 'utf8' }) + t.same(await db.get(Buffer.from('foo🐄'), { keyEncoding: 'utf8' }), 'test') + return db.close() + }) + + // NOTE: adapted from levelup + test('test put() and get() with string value and buffer valueEncoding', async function (t) { + const db = testCommon.factory() + await db.open() + await db.put('test', 'foo🐄', { valueEncoding: 'buffer' }) + t.same(await db.get('test', { valueEncoding: 'buffer' }), Buffer.from('foo🐄')) + t.same(await db.get('test', { valueEncoding: 'utf8' }), 'foo🐄') + return db.close() + }) + + // NOTE: adapted from memdown + test('put() as string, get() as buffer and vice versa', async function (t) { + const db = testCommon.factory() + await db.open() + const enc = { keyEncoding: 'buffer', valueEncoding: 'buffer' } + const [a, b] = ['🐄', '🐄 says moo'] + + const promise1 = db.put(a, a).then(async () => { + const value = await db.get(Buffer.from(a), enc) + t.same(value, Buffer.from(a), 'got buffer value') + }) + + const promise2 = db.put(Buffer.from(b), Buffer.from(b), enc).then(async () => { + const value = await db.get(b) + t.same(value, b, 'got string value') + }) + + await Promise.all([promise1, promise2]) + return db.close() + }) + + // NOTE: adapted from memdown + test('put() stringifies input to buffer', async function (t) { + const db = testCommon.factory() + await db.open() + await db.put(1, 2) + + const it = db.iterator({ keyEncoding: 'buffer', valueEncoding: 'buffer' }) + const entries = await it.all() + + t.same(entries[0][0], Buffer.from('1'), 'key was stringified') + t.same(entries[0][1], Buffer.from('2'), 'value was stringified') + + return db.close() + }) + + // NOTE: adapted from memdown + test('put() as string, iterate as buffer', async function (t) { + const db = testCommon.factory({ keyEncoding: 'utf8', valueEncoding: 'utf8' }) + await db.open() + await db.put('🐄', '🐄') + + const it = db.iterator({ keyEncoding: 'buffer', valueEncoding: 'buffer' }) + const entries = await it.all() + + t.same(entries, [[Buffer.from('🐄'), Buffer.from('🐄')]]) + return db.close() + }) + + // NOTE: adapted from memdown + test('put() as buffer, iterate as string', async function (t) { + const db = testCommon.factory({ keyEncoding: 'buffer', valueEncoding: 'buffer' }) + await db.open() + await db.put(Buffer.from('🐄'), Buffer.from('🐄')) + + const it = db.iterator({ keyEncoding: 'utf8', valueEncoding: 'utf8' }) + const entries = await it.all() + + t.same(entries, [['🐄', '🐄']]) + return db.close() + }) + + test('put() as view, iterate as view', async function (t) { + const db = testCommon.factory({ keyEncoding: 'view', valueEncoding: 'view' }) + const cow = textEncoder.encode('🐄') + await db.open() + await db.put(cow, cow) + + const it = db.iterator() + const entries = await it.all() + const key = Buffer.isBuffer(entries[0][0]) ? Buffer.from(cow) : cow // Valid, Buffer is a Uint8Array + const value = Buffer.isBuffer(entries[0][1]) ? Buffer.from(cow) : cow + + t.same(entries, [[key, value]]) + return db.close() + }) + + test('put() as string, iterate as view', async function (t) { + const db = testCommon.factory({ keyEncoding: 'utf8', valueEncoding: 'utf8' }) + const cow = textEncoder.encode('🐄') + await db.open() + await db.put('🐄', '🐄') + + const it = db.iterator({ keyEncoding: 'view', valueEncoding: 'view' }) + const entries = await it.all() + const key = Buffer.isBuffer(entries[0][0]) ? Buffer.from(cow) : cow // Valid, Buffer is a Uint8Array + const value = Buffer.isBuffer(entries[0][1]) ? Buffer.from(cow) : cow + + t.same(entries, [[key, value]]) + return db.close() + }) + + test('put() as view, iterate as string', async function (t) { + const db = testCommon.factory({ keyEncoding: 'view', valueEncoding: 'view' }) + const cow = textEncoder.encode('🐄') + await db.open() + await db.put(cow, cow) + + const it = db.iterator({ keyEncoding: 'utf8', valueEncoding: 'utf8' }) + const entries = await it.all() + + t.same(entries, [['🐄', '🐄']]) + return db.close() + }) + + // NOTE: adapted from levelup + test('batch() with multiple puts with buffer valueEncoding per batch', async function (t) { + const db = testCommon.factory() + await db.open() + await db.batch([ + { type: 'put', key: 'foo', value: testBuffer() }, + { type: 'put', key: 'bar', value: testBuffer() }, + { type: 'put', key: 'baz', value: 'abazvalue' } + ], { valueEncoding: 'buffer' }) + + t.same(await db.get('foo', { valueEncoding: 'buffer' }), testBuffer()) + t.same(await db.get('bar', { valueEncoding: 'buffer' }), testBuffer()) + t.same(await db.get('baz', { valueEncoding: 'buffer' }), Buffer.from('abazvalue')) + + return db.close() + }) + + test('batch() with multiple puts with buffer valueEncoding per operation', async function (t) { + const db = testCommon.factory() + await db.open() + await db.batch([ + { type: 'put', key: 'foo', value: testBuffer(), valueEncoding: 'buffer' }, + { type: 'put', key: 'bar', value: testBuffer(), valueEncoding: 'buffer' }, + { type: 'put', key: 'baz', value: 'abazvalue', valueEncoding: 'buffer' } + ]) + + t.same(await db.get('foo', { valueEncoding: 'buffer' }), testBuffer()) + t.same(await db.get('bar', { valueEncoding: 'buffer' }), testBuffer()) + t.same(await db.get('baz', { valueEncoding: 'buffer' }), Buffer.from('abazvalue')) + + return db.close() + }) + + // NOTE: adapted from encoding-down + test('batch() with buffer encoding in factory', async function (t) { + const operations = [{ + type: 'put', + key: Buffer.from([1, 2, 3]), + value: Buffer.from([4, 5, 6]) + }, { + type: 'put', + key: Buffer.from([7, 8, 9]), + value: Buffer.from([10, 11, 12]) + }] + + const db = testCommon.factory({ keyEncoding: 'buffer', valueEncoding: 'buffer' }) + await db.open() + await db.batch(operations) + + t.same(await db.get(operations[0].key), operations[0].value) + t.same(await db.get(operations[1].key), operations[1].value) + + return db.close() + }) + + for (const keyEncoding of ['buffer', 'view']) { + // NOTE: adapted from memdown + test(`storage is byte-aware (${keyEncoding} encoding)`, function (t) { + const db = testCommon.factory({ keyEncoding }) + + db.open(function (err) { + t.ifError(err, 'no open error') + + const one = Buffer.from('80', 'hex') + const two = Buffer.from('c0', 'hex') + + t.ok(two.toString() === one.toString(), 'would be equal when not byte-aware') + t.ok(two.compare(one) > 0, 'but greater when byte-aware') + + db.put(one, 'one', function (err) { + t.ifError(err, 'no put() error') + + db.get(one, function (err, value) { + t.ifError(err, 'no get() error') + t.is(value, 'one', 'value one ok') + + db.put(two, 'two', function (err) { + t.ifError(err, 'no put() error') + + db.get(one, function (err, value) { + t.ifError(err, 'no get() error') + t.is(value, 'one', 'value one did not change') + + db.close(t.end.bind(t)) + }) + }) + }) + }) + }) + }) + } +} + +function testBuffer () { + return Buffer.from('0080c0ff', 'hex') +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-custom-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-custom-test.js new file mode 100644 index 0000000..f0ff27a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-custom-test.js @@ -0,0 +1,99 @@ +'use strict' + +// NOTE: copied from levelup +exports.all = function (test, testCommon) { + for (const deferred of [false, true]) { + test(`custom encoding: simple-object values (deferred: ${deferred})`, function (t) { + run(t, deferred, [ + { key: '0', value: 0 }, + { key: '1', value: 1 }, + { key: 'string', value: 'a string' }, + { key: 'true', value: true }, + { key: 'false', value: false } + ]) + }) + + test(`custom encoding: simple-object keys (deferred: ${deferred})`, function (t) { + // Test keys that would be considered the same with default utf8 encoding. + // Because String([1]) === String(1). + run(t, deferred, [ + { value: '0', key: [1] }, + { value: '1', key: 1 }, + { value: 'string', key: 'a string' }, + { value: 'true', key: true }, + { value: 'false', key: false } + ]) + }) + + test(`custom encoding: complex-object values (deferred: ${deferred})`, function (t) { + run(t, deferred, [{ + key: '0', + value: { + foo: 'bar', + bar: [1, 2, 3], + bang: { yes: true, no: false } + } + }]) + }) + + test(`custom encoding: complex-object keys (deferred: ${deferred})`, function (t) { + // Test keys that would be considered the same with default utf8 encoding. + // Because String({}) === String({}) === '[object Object]'. + run(t, deferred, [{ + value: '0', + key: { + foo: 'bar', + bar: [1, 2, 3], + bang: { yes: true, no: false } + } + }, { + value: '1', + key: { + foo: 'different', + bar: [1, 2, 3], + bang: { yes: true, no: false } + } + }]) + }) + } + + function run (t, deferred, entries) { + const customEncoding = { + encode: JSON.stringify, + decode: JSON.parse, + format: 'utf8', + type: 'custom' + } + + const db = testCommon.factory({ + keyEncoding: customEncoding, + valueEncoding: customEncoding + }) + + const operations = entries.map(entry => ({ type: 'put', ...entry })) + const init = deferred ? (fn) => fn() : db.open.bind(db) + + init(function (err) { + t.ifError(err, 'no init() error') + + db.batch(operations, function (err) { + t.ifError(err, 'no batch() error') + + let pending = entries.length + const next = () => { + if (--pending === 0) { + db.close(t.end.bind(t)) + } + } + + for (const entry of entries) { + db.get(entry.key, function (err, value) { + t.ifError(err, 'no get() error') + t.same(value, entry.value) + next() + }) + } + }) + }) + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-decode-error-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-decode-error-test.js new file mode 100644 index 0000000..e2a6986 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-decode-error-test.js @@ -0,0 +1,64 @@ +'use strict' + +let db +let keySequence = 0 + +const testKey = () => 'test' + (++keySequence) + +exports.all = function (test, testCommon) { + test('setup', async function (t) { + db = testCommon.factory() + return db.open() + }) + + // NOTE: adapted from encoding-down + test('get() and getMany() forward decode error', function (t) { + const key = testKey() + const valueEncoding = { + encode: (v) => v, + decode: (v) => { throw new Error('decode error xyz') }, + format: 'utf8' + } + + db.put(key, 'bar', { valueEncoding }, function (err) { + t.ifError(err, 'no put() error') + + db.get(key, { valueEncoding }, function (err, value) { + t.is(err && err.code, 'LEVEL_DECODE_ERROR') + t.is(err && err.cause && err.cause.message, 'decode error xyz') + t.is(value, undefined) + + db.getMany(['other-key', key], { valueEncoding }, function (err, values) { + t.is(err && err.code, 'LEVEL_DECODE_ERROR') + t.is(err && err.cause && err.cause.message, 'decode error xyz') + t.is(values, undefined) + t.end() + }) + }) + }) + }) + + // NOTE: adapted from encoding-down + test('get() and getMany() yield encoding error if stored value is invalid', function (t) { + const key = testKey() + + db.put(key, 'this {} is [] not : json', { valueEncoding: 'utf8' }, function (err) { + t.ifError(err, 'no put() error') + + db.get(key, { valueEncoding: 'json' }, function (err) { + t.is(err && err.code, 'LEVEL_DECODE_ERROR') + t.is(err && err.cause.name, 'SyntaxError') // From JSON.parse() + + db.getMany(['other-key', key], { valueEncoding: 'json' }, function (err) { + t.is(err && err.code, 'LEVEL_DECODE_ERROR') + t.is(err && err.cause.name, 'SyntaxError') // From JSON.parse() + t.end() + }) + }) + }) + }) + + test('teardown', async function (t) { + return db.close() + }) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-json-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-json-test.js new file mode 100644 index 0000000..bb744f9 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-json-test.js @@ -0,0 +1,88 @@ +'use strict' + +// NOTE: copied from levelup +exports.all = function (test, testCommon) { + for (const deferred of [false, true]) { + test(`json encoding: simple-object values (deferred: ${deferred})`, function (t) { + run(t, deferred, [ + { key: '0', value: 0 }, + { key: '1', value: 1 }, + { key: '2', value: 'a string' }, + { key: '3', value: true }, + { key: '4', value: false } + ]) + }) + + test(`json encoding: simple-object keys (deferred: ${deferred})`, function (t) { + run(t, deferred, [ + { value: 'string', key: 'a string' }, + { value: '0', key: 0 }, + { value: '1', key: 1 }, + { value: 'false', key: false }, + { value: 'true', key: true } + ]) + }) + + test(`json encoding: complex-object values (deferred: ${deferred})`, function (t) { + run(t, deferred, [{ + key: '0', + value: { + foo: 'bar', + bar: [1, 2, 3], + bang: { yes: true, no: false } + } + }]) + }) + + test(`json encoding: complex-object keys (deferred: ${deferred})`, function (t) { + run(t, deferred, [{ + value: '0', + key: { + foo: 'bar', + bar: [1, 2, 3], + bang: { yes: true, no: false } + } + }]) + }) + } + + function run (t, deferred, entries) { + const db = testCommon.factory({ keyEncoding: 'json', valueEncoding: 'json' }) + const operations = entries.map(entry => ({ type: 'put', ...entry })) + const init = deferred ? (fn) => fn() : db.open.bind(db) + + init(function (err) { + t.ifError(err, 'no init() error') + + db.batch(operations, function (err) { + t.ifError(err, 'no batch() error') + + let pending = entries.length + 1 + const next = () => { + if (--pending === 0) db.close(t.end.bind(t)) + } + + testGet(next) + testIterator(next) + }) + }) + + function testGet (next) { + for (const entry of entries) { + db.get(entry.key, function (err, value) { + t.ifError(err, 'no get() error') + t.same(value, entry.value) + next() + }) + } + } + + function testIterator (next) { + db.iterator().all(function (err, result) { + t.ifError(err, 'no all() error') + t.same(result, entries.map(kv => [kv.key, kv.value])) + next() + }) + } + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-test.js new file mode 100644 index 0000000..e9969a8 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/encoding-test.js @@ -0,0 +1,141 @@ +'use strict' + +let db +let keySequence = 0 + +const testKey = () => 'test' + (++keySequence) + +// TODO: test encoding options on every method. This is largely +// covered (indirectly) by other tests, but a dedicated property- +// based test for each would be good to have. +exports.all = function (test, testCommon) { + test('setup', async function (t) { + db = testCommon.factory() + return db.open() + }) + + // NOTE: adapted from encoding-down + test('encodings default to utf8', function (t) { + t.is(db.keyEncoding().commonName, 'utf8') + t.is(db.valueEncoding().commonName, 'utf8') + t.end() + }) + + test('can set encoding options in factory', async function (t) { + const dbs = [] + + for (const name of ['buffer', 'view', 'json']) { + if (!testCommon.supports.encodings[name]) continue + + const db1 = testCommon.factory({ keyEncoding: name }) + const db2 = testCommon.factory({ valueEncoding: name }) + const db3 = testCommon.factory({ keyEncoding: name, valueEncoding: name }) + + t.is(db1.keyEncoding().commonName, name) + t.is(db1.keyEncoding(), db1.keyEncoding(name)) + t.is(db1.valueEncoding().commonName, 'utf8') + t.is(db1.valueEncoding(), db1.valueEncoding('utf8')) + + t.is(db2.keyEncoding().commonName, 'utf8') + t.is(db2.keyEncoding(), db2.keyEncoding('utf8')) + t.is(db2.valueEncoding().commonName, name) + t.is(db2.valueEncoding(), db2.valueEncoding(name)) + + t.is(db3.keyEncoding().commonName, name) + t.is(db3.keyEncoding(), db3.keyEncoding(name)) + t.is(db3.valueEncoding().commonName, name) + t.is(db3.valueEncoding(), db3.valueEncoding(name)) + + dbs.push(db1, db2, db3) + } + + await Promise.all(dbs.map(db => db.close())) + }) + + // NOTE: adapted from encoding-down + for (const deferred of [false, true]) { + test(`default utf8 encoding stringifies numbers (deferred: ${deferred})`, async function (t) { + const db = testCommon.factory() + if (!deferred) await db.open() + await db.put(1, 2) + t.is(await db.get(1), '2') + return db.close() + }) + } + + // NOTE: adapted from encoding-down + test('can decode from string to json', function (t) { + const key = testKey() + const data = { thisis: 'json' } + + db.put(key, JSON.stringify(data), { valueEncoding: 'utf8' }, function (err) { + t.ifError(err, 'no put() error') + + db.get(key, { valueEncoding: 'json' }, function (err, value) { + t.ifError(err, 'no get() error') + t.same(value, data, 'got parsed object') + t.end() + }) + }) + }) + + // NOTE: adapted from encoding-down + test('can decode from json to string', function (t) { + const data = { thisis: 'json' } + const key = testKey() + + db.put(key, data, { valueEncoding: 'json' }, function (err) { + t.ifError(err, 'no put() error') + + db.get(key, { valueEncoding: 'utf8' }, function (err, value) { + t.ifError(err, 'no get() error') + t.is(value, JSON.stringify(data), 'got unparsed JSON string') + t.end() + }) + }) + }) + + // NOTE: adapted from encoding-down + test('getMany() skips decoding not-found values', function (t) { + t.plan(4) + + const valueEncoding = { + encode: JSON.stringify, + decode (value) { + t.is(value, JSON.stringify(data)) + return JSON.parse(value) + }, + format: 'utf8' + } + + const data = { beep: 'boop' } + const key = testKey() + + db.put(key, data, { valueEncoding }, function (err) { + t.ifError(err, 'no put() error') + + db.getMany([key, testKey()], { valueEncoding }, function (err, values) { + t.ifError(err, 'no getMany() error') + t.same(values, [data, undefined]) + }) + }) + }) + + // NOTE: adapted from memdown + test('number keys with utf8 encoding', async function (t) { + const db = testCommon.factory() + const numbers = [-Infinity, 0, 12, 2, +Infinity] + + await db.open() + await db.batch(numbers.map(key => ({ type: 'put', key, value: 'value' }))) + + const keys = await db.keys({ keyEncoding: 'utf8' }).all() + t.same(keys, numbers.map(String), 'sorts lexicographically') + + return db.close() + }) + + test('teardown', async function (t) { + return db.close() + }) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/factory-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/factory-test.js new file mode 100644 index 0000000..519b84e --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/factory-test.js @@ -0,0 +1,52 @@ +'use strict' + +module.exports = function (test, testCommon) { + test('testCommon.factory() returns valid database', function (t) { + t.plan(6) + + const db = testCommon.factory() + const kEvent = Symbol('event') + + // Avoid instanceof, for levelup compatibility tests + t.is(typeof db, 'object', 'is an object') + t.isNot(db, null, 'is not null') + t.is(typeof db.open, 'function', 'has open() method') + t.is(typeof db.on, 'function', 'has on() method') + t.is(typeof db.emit, 'function', 'has emit() method') + + db.once(kEvent, (v) => t.is(v, 'foo', 'got event')) + db.emit(kEvent, 'foo') + }) + + test('testCommon.factory() returns a unique database', function (t) { + const db1 = testCommon.factory() + const db2 = testCommon.factory() + + t.isNot(db1, db2, 'unique instances') + + function close () { + db1.close(function (err) { + t.error(err, 'no error while closing db1') + db2.close(function (err) { + t.error(err, 'no error while closing db2') + t.end() + }) + }) + } + + db1.open(function (err) { + t.error(err, 'no error while opening db1') + db2.open(function (err) { + t.error(err, 'no error while opening db2') + db1.put('key', 'value', function (err) { + t.error(err, 'put key in db1') + db2.get('key', function (err, value) { + t.ok(err, 'db2 should be empty') + t.is(value, undefined, 'db2 should be empty') + close() + }) + }) + }) + }) + }) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/get-many-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/get-many-test.js new file mode 100644 index 0000000..2221e69 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/get-many-test.js @@ -0,0 +1,276 @@ +'use strict' + +const { assertAsync, illegalKeys } = require('./util') + +let db + +/** + * @param {import('tape')} test + */ +exports.setUp = function (test, testCommon) { + test('setUp db', function (t) { + db = testCommon.factory() + db.open(t.end.bind(t)) + }) +} + +/** + * @param {import('tape')} test + */ +exports.args = function (test, testCommon) { + test('test getMany() requires an array argument (callback)', assertAsync.ctx(function (t) { + // Add 1 assertion for every assertAsync() + t.plan(6) + + db.getMany('foo', assertAsync(function (err) { + t.is(err.name, 'TypeError') + t.is(err && err.message, "The first argument 'keys' must be an array") + })) + db.getMany('foo', {}, assertAsync(function (err) { + t.is(err.name, 'TypeError') + t.is(err && err.message, "The first argument 'keys' must be an array") + })) + })) + + test('test getMany() requires an array argument (promise)', function (t) { + t.plan(6) + + db.getMany().catch(function (err) { + t.is(err.name, 'TypeError') + t.is(err && err.message, "The first argument 'keys' must be an array") + }) + db.getMany('foo').catch(function (err) { + t.is(err.name, 'TypeError') + t.is(err && err.message, "The first argument 'keys' must be an array") + }) + db.getMany('foo', {}).catch(function (err) { + t.is(err.name, 'TypeError') + t.is(err && err.message, "The first argument 'keys' must be an array") + }) + }) + + test('test getMany() with illegal keys', assertAsync.ctx(function (t) { + // Add 1 assertion for every assertAsync() + t.plan(illegalKeys.length * 10) + + for (const { name, key } of illegalKeys) { + db.getMany([key], assertAsync(function (err) { + t.ok(err instanceof Error, name + ' - is Error (callback)') + t.is(err && err.code, 'LEVEL_INVALID_KEY', name + ' - correct error code (callback)') + })) + + db.getMany(['valid', key], assertAsync(function (err) { + t.ok(err instanceof Error, name + ' - is Error (callback, second key)') + t.is(err && err.code, 'LEVEL_INVALID_KEY', name + ' - correct error code (callback, second key)') + })) + + db.getMany([key]).catch(function (err) { + t.ok(err instanceof Error, name + ' - is Error (promise)') + t.is(err.code, 'LEVEL_INVALID_KEY', name + ' - correct error code (promise)') + }) + + db.getMany(['valid', key]).catch(function (err) { + t.ok(err instanceof Error, name + ' - is Error (promise, second key)') + t.is(err.code, 'LEVEL_INVALID_KEY', name + ' - correct error code (promise, second key)') + }) + } + })) +} + +/** + * @param {import('tape')} test + */ +exports.getMany = function (test, testCommon) { + test('test simple getMany()', function (t) { + db.put('foo', 'bar', function (err) { + t.error(err) + + function verify (err, values) { + t.error(err) + t.ok(Array.isArray(values), 'got an array') + t.is(values.length, 1, 'array has 1 element') + t.is(values[0], 'bar') + } + + db.getMany(['foo'], function (err, values) { + verify(err, values) + + db.getMany(['foo'], {}, function (err, values) { + verify(err, values) + + db.getMany(['foo'], { valueEncoding: 'utf8' }, function (err, values) { + t.error(err) + t.is(values && typeof values[0], 'string', 'should be string if not buffer') + t.same(values, ['bar']) + t.end() + }) + }) + }) + }) + }) + + test('test getMany() with multiple keys', function (t) { + t.plan(5) + + db.put('beep', 'boop', function (err) { + t.ifError(err) + + db.getMany(['foo', 'beep'], { valueEncoding: 'utf8' }, function (err, values) { + t.ifError(err) + t.same(values, ['bar', 'boop']) + }) + + db.getMany(['beep', 'foo'], { valueEncoding: 'utf8' }, function (err, values) { + t.ifError(err) + t.same(values, ['boop', 'bar'], 'maintains order of input keys') + }) + }) + }) + + test('test empty getMany()', assertAsync.ctx(function (t) { + const encodings = Object.keys(db.supports.encodings).filter(k => db.supports.encodings[k]) + t.plan(encodings.length * 3) + + for (const valueEncoding of encodings) { + db.getMany([], { valueEncoding }, assertAsync(function (err, values) { + t.ifError(err) + t.same(values, []) + })) + } + })) + + test('test not-found getMany()', assertAsync.ctx(function (t) { + const encodings = Object.keys(db.supports.encodings).filter(k => db.supports.encodings[k]) + t.plan(encodings.length * 3) + + for (const valueEncoding of encodings) { + db.getMany(['nope', 'another'], { valueEncoding }, assertAsync(function (err, values) { + t.ifError(err) + t.same(values, [undefined, undefined]) + })) + } + })) + + test('test getMany() with promise', async function (t) { + t.same(await db.getMany(['foo'], { valueEncoding: 'utf8' }), ['bar']) + t.same(await db.getMany(['beep'], { valueEncoding: 'utf8' }), ['boop']) + t.same(await db.getMany(['foo', 'beep'], { valueEncoding: 'utf8' }), ['bar', 'boop']) + t.same(await db.getMany(['beep', 'foo'], { valueEncoding: 'utf8' }), ['boop', 'bar']) + t.same(await db.getMany(['beep', 'foo', 'nope'], { valueEncoding: 'utf8' }), ['boop', 'bar', undefined]) + t.same(await db.getMany([], { valueEncoding: 'utf8' }), []) + }) + + test('test simultaneous getMany()', function (t) { + db.put('hello', 'world', function (err) { + t.error(err) + + let completed = 0 + const done = function () { + if (++completed === 20) t.end() + } + + for (let i = 0; i < 10; ++i) { + db.getMany(['hello'], function (err, values) { + t.error(err) + t.is(values.length, 1) + t.is(values[0] && values[0].toString(), 'world') + done() + }) + } + + for (let i = 0; i < 10; ++i) { + db.getMany(['not found'], function (err, values) { + t.error(err) + t.same(values, [undefined]) + done() + }) + } + }) + }) + + test('test getMany() on opening db', assertAsync.ctx(function (t) { + t.plan(2 * 2 * 5) + + // Also test empty array because it has a fast-path + for (const keys of [['foo'], []]) { + // Opening should make no difference, because we call it after getMany() + for (const open of [true, false]) { + const db = testCommon.factory() + + t.is(db.status, 'opening') + + db.getMany(keys, assertAsync(function (err, values) { + t.ifError(err, 'no error') + t.same(values, keys.map(_ => undefined)) + })) + + if (open) { + db.open(t.error.bind(t)) + } else { + t.pass() + } + } + } + })) + + test('test getMany() on closed db', function (t) { + t.plan(2 * 4) + + // Also test empty array because it has a fast-path + for (const keys of [['foo'], []]) { + const db = testCommon.factory() + + db.open(function (err) { + t.ifError(err) + + db.close(assertAsync.with(t, function (err) { + t.ifError(err) + + db.getMany(keys, assertAsync(function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + })) + })) + }) + } + }) + + test('test getMany() on closing db', function (t) { + t.plan(2 * 4) + + // Also test empty array because it has a fast-path + for (const keys of [['foo'], []]) { + const db = testCommon.factory() + + db.open(assertAsync.with(t, function (err) { + t.ifError(err) + + db.close(function (err) { + t.ifError(err) + }) + + db.getMany(keys, assertAsync(function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + })) + })) + } + }) +} + +/** + * @param {import('tape')} test + */ +exports.tearDown = function (test, testCommon) { + test('tearDown', function (t) { + db.close(t.end.bind(t)) + }) +} + +/** + * @param {import('tape')} test + */ +exports.all = function (test, testCommon) { + exports.setUp(test, testCommon) + exports.args(test, testCommon) + exports.getMany(test, testCommon) + exports.tearDown(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/get-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/get-test.js new file mode 100644 index 0000000..8d746c0 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/get-test.js @@ -0,0 +1,144 @@ +'use strict' + +const isBuffer = require('is-buffer') +const { verifyNotFoundError, illegalKeys, assertAsync } = require('./util') + +let db + +exports.setUp = function (test, testCommon) { + test('setUp db', function (t) { + db = testCommon.factory() + db.open(t.end.bind(t)) + }) +} + +exports.args = function (test, testCommon) { + test('test get() with illegal keys', assertAsync.ctx(function (t) { + t.plan(illegalKeys.length * 5) + + for (const { name, key } of illegalKeys) { + db.get(key, assertAsync(function (err) { + t.ok(err instanceof Error, name + ' - is Error (callback)') + t.is(err && err.code, 'LEVEL_INVALID_KEY', name + ' - correct error code (callback)') + })) + + db.get(key).catch(function (err) { + t.ok(err instanceof Error, name + ' - is Error (promise)') + t.is(err.code, 'LEVEL_INVALID_KEY', name + ' - correct error code (promise)') + }) + } + })) +} + +exports.get = function (test, testCommon) { + test('test simple get()', function (t) { + db.put('foo', 'bar', function (err) { + t.error(err) + db.get('foo', function (err, value) { + t.error(err) + t.is(value, 'bar') + + db.get('foo', {}, function (err, value) { // same but with {} + t.error(err) + t.is(value, 'bar') + + db.get('foo', { valueEncoding: 'utf8' }, function (err, value) { + t.error(err) + t.is(value, 'bar') + + if (!db.supports.encodings.buffer) { + return t.end() + } + + db.get('foo', { valueEncoding: 'buffer' }, function (err, value) { + t.error(err) + t.ok(isBuffer(value), 'should be buffer') + t.is(value.toString(), 'bar') + t.end() + }) + }) + }) + }) + }) + }) + + test('test get() with promise', function (t) { + db.put('promises', 'yes', function (err) { + t.error(err) + + db.get('promises').then(function (value) { + t.is(value, 'yes', 'got value without options') + + db.get('not found').catch(function (err) { + t.ok(err, 'should error') + t.ok(verifyNotFoundError(err), 'correct error') + + if (!db.supports.encodings.buffer) { + return t.end() + } + + db.get('promises', { valueEncoding: 'buffer' }).then(function (value) { + t.ok(isBuffer(value), 'is buffer') + t.is(value.toString(), 'yes', 'correct value') + t.end() + }).catch(t.fail.bind(t)) + }) + }).catch(t.fail.bind(t)) + }) + }) + + test('test simultaneous get()', function (t) { + db.put('hello', 'world', function (err) { + t.error(err) + let completed = 0 + const done = function () { + if (++completed === 20) t.end() + } + + for (let i = 0; i < 10; ++i) { + db.get('hello', function (err, value) { + t.error(err) + t.is(value.toString(), 'world') + done() + }) + } + + for (let i = 0; i < 10; ++i) { + db.get('not found', function (err, value) { + t.ok(err, 'should error') + t.ok(verifyNotFoundError(err), 'correct error') + t.ok(typeof value === 'undefined', 'value is undefined') + done() + }) + } + }) + }) + + test('test get() not found error is asynchronous', function (t) { + t.plan(4) + + let async = false + + db.get('not found', function (err, value) { + t.ok(err, 'should error') + t.ok(verifyNotFoundError(err), 'correct error') + t.ok(typeof value === 'undefined', 'value is undefined') + t.ok(async, 'callback is asynchronous') + }) + + async = true + }) +} + +exports.tearDown = function (test, testCommon) { + test('tearDown', function (t) { + db.close(t.end.bind(t)) + }) +} + +exports.all = function (test, testCommon) { + exports.setUp(test, testCommon) + exports.args(test, testCommon) + exports.get(test, testCommon) + exports.tearDown(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/index.js new file mode 100644 index 0000000..d56b4a8 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/index.js @@ -0,0 +1,72 @@ +'use strict' + +const common = require('./common') +const kSublevels = Symbol('sublevels') + +function suite (options) { + const testCommon = common(options) + const test = testCommon.test + + require('./factory-test')(test, testCommon) + require('./manifest-test')(test, testCommon) + require('./open-test').all(test, testCommon) + require('./close-test').all(test, testCommon) + + if (testCommon.supports.createIfMissing) { + require('./open-create-if-missing-test').all(test, testCommon) + } + + if (testCommon.supports.errorIfExists) { + require('./open-error-if-exists-test').all(test, testCommon) + } + + require('./put-test').all(test, testCommon) + require('./get-test').all(test, testCommon) + require('./del-test').all(test, testCommon) + require('./put-get-del-test').all(test, testCommon) + require('./get-many-test').all(test, testCommon) + + require('./batch-test').all(test, testCommon) + require('./chained-batch-test').all(test, testCommon) + + require('./iterator-test').all(test, testCommon) + require('./iterator-range-test').all(test, testCommon) + require('./async-iterator-test').all(test, testCommon) + + require('./deferred-open-test').all(test, testCommon) + require('./encoding-test').all(test, testCommon) + require('./encoding-json-test').all(test, testCommon) + require('./encoding-custom-test').all(test, testCommon) + require('./encoding-buffer-test').all(test, testCommon) + require('./encoding-decode-error-test').all(test, testCommon) + + if (testCommon.supports.seek) { + require('./iterator-seek-test').all(test, testCommon) + } + + if (testCommon.supports.snapshots) { + require('./iterator-snapshot-test').all(test, testCommon) + } else { + require('./iterator-no-snapshot-test').all(test, testCommon) + } + + require('./clear-test').all(test, testCommon) + require('./clear-range-test').all(test, testCommon) + require('./sublevel-test').all(test, testCommon) + + // Run the same suite on a sublevel + if (!testCommon.internals[kSublevels]) { + const factory = testCommon.factory + + suite({ + ...testCommon, + internals: { [kSublevels]: true }, + factory (opts) { + return factory().sublevel('test', opts) + } + }) + } +} + +suite.common = common +module.exports = suite diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-no-snapshot-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-no-snapshot-test.js new file mode 100644 index 0000000..1ea5b12 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-no-snapshot-test.js @@ -0,0 +1,63 @@ +'use strict' + +exports.noSnapshot = function (test, testCommon) { + function make (run) { + return function (t) { + const db = testCommon.factory() + const operations = [ + { type: 'put', key: 'a', value: 'a' }, + { type: 'put', key: 'b', value: 'b' }, + { type: 'put', key: 'c', value: 'c' } + ] + + db.open(function (err) { + t.ifError(err, 'no open error') + + db.batch(operations, function (err) { + t.ifError(err, 'no batch error') + + // For this test it is important that we don't read eagerly. + // NOTE: highWaterMarkBytes is not an abstract option, but + // it is supported by classic-level and others. Also set the + // old & equivalent leveldown highWaterMark option for compat. + const it = db.iterator({ highWaterMarkBytes: 0, highWaterMark: 0 }) + + run(db, function (err) { + t.ifError(err, 'no run error') + verify(t, it, db) + }) + }) + }) + } + } + + function verify (t, it, db) { + it.all(function (err, entries) { + t.ifError(err, 'no iterator error') + + const kv = entries.map(function ([key, value]) { + return key.toString() + value.toString() + }) + + if (kv.length === 3) { + t.same(kv, ['aa', 'bb', 'cc'], 'maybe supports snapshots') + } else { + t.same(kv, ['aa', 'cc'], 'ignores keys that have been deleted in the mean time') + } + + db.close(t.end.bind(t)) + }) + } + + test('delete key after creating iterator', make(function (db, done) { + db.del('b', done) + })) + + test('batch delete key after creating iterator', make(function (db, done) { + db.batch([{ type: 'del', key: 'b' }], done) + })) +} + +exports.all = function (test, testCommon) { + exports.noSnapshot(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-range-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-range-test.js new file mode 100644 index 0000000..8ef8ac6 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-range-test.js @@ -0,0 +1,287 @@ +'use strict' + +let db + +const data = (function () { + const d = [] + let i = 0 + let k + for (; i < 100; i++) { + k = (i < 10 ? '0' : '') + i + d.push({ + key: k, + value: String(Math.random()) + }) + } + return d +}()) + +exports.setUp = function (test, testCommon) { + test('setUp db', function (t) { + db = testCommon.factory() + db.open(function () { + db.batch(data.map(function (d) { + return { + type: 'put', + key: d.key, + value: d.value + } + }), t.end.bind(t)) + }) + }) +} + +exports.range = function (test, testCommon) { + function rangeTest (name, opts, expected) { + opts.keyEncoding = 'utf8' + opts.valueEncoding = 'utf8' + + test(name, function (t) { + db.iterator(opts).all(function (err, entries) { + t.error(err) + t.is(entries.length, expected.length, 'correct number of entries') + t.same(entries, expected.map(o => [o.key, o.value])) + t.end() + }) + }) + + // Test the documented promise that in reverse mode, + // "the returned entries are the same, but in reverse". + if (!opts.reverse && !('limit' in opts)) { + const reverseOpts = Object.assign({}, opts, { reverse: true }) + + rangeTest( + name + ' (flipped)', + reverseOpts, + expected.slice().reverse() + ) + } + } + + rangeTest('test full data collection', {}, data) + + rangeTest('test iterator with reverse=true', { + reverse: true + }, data.slice().reverse()) + + rangeTest('test iterator with gte=00', { + gte: '00' + }, data) + + rangeTest('test iterator with gte=50', { + gte: '50' + }, data.slice(50)) + + rangeTest('test iterator with lte=50 and reverse=true', { + lte: '50', + reverse: true + }, data.slice().reverse().slice(49)) + + rangeTest('test iterator with gte=49.5 (midway)', { + gte: '49.5' + }, data.slice(50)) + + rangeTest('test iterator with gte=49999 (midway)', { + gte: '49999' + }, data.slice(50)) + + rangeTest('test iterator with lte=49.5 (midway) and reverse=true', { + lte: '49.5', + reverse: true + }, data.slice().reverse().slice(50)) + + rangeTest('test iterator with lt=49.5 (midway) and reverse=true', { + lt: '49.5', + reverse: true + }, data.slice().reverse().slice(50)) + + rangeTest('test iterator with lt=50 and reverse=true', { + lt: '50', + reverse: true + }, data.slice().reverse().slice(50)) + + rangeTest('test iterator with lte=50', { + lte: '50' + }, data.slice(0, 51)) + + rangeTest('test iterator with lte=50.5 (midway)', { + lte: '50.5' + }, data.slice(0, 51)) + + rangeTest('test iterator with lte=50555 (midway)', { + lte: '50555' + }, data.slice(0, 51)) + + rangeTest('test iterator with lt=50555 (midway)', { + lt: '50555' + }, data.slice(0, 51)) + + rangeTest('test iterator with gte=50.5 (midway) and reverse=true', { + gte: '50.5', + reverse: true + }, data.slice().reverse().slice(0, 49)) + + rangeTest('test iterator with gt=50.5 (midway) and reverse=true', { + gt: '50.5', + reverse: true + }, data.slice().reverse().slice(0, 49)) + + rangeTest('test iterator with gt=50 and reverse=true', { + gt: '50', + reverse: true + }, data.slice().reverse().slice(0, 49)) + + // first key is actually '00' so it should avoid it + rangeTest('test iterator with lte=0', { + lte: '0' + }, []) + + // first key is actually '00' so it should avoid it + rangeTest('test iterator with lt=0', { + lt: '0' + }, []) + + rangeTest('test iterator with gte=30 and lte=70', { + gte: '30', + lte: '70' + }, data.slice(30, 71)) + + // The gte and lte options should take precedence over gt and lt respectively. + rangeTest('test iterator with gte=30 and lte=70 and gt=40 and lt=60', { + gte: '30', + lte: '70', + gt: '40', + lt: '60' + }, data.slice(30, 71)) + + // Also test the other way around: if gt and lt were to select a bigger range. + rangeTest('test iterator with gte=30 and lte=70 and gt=20 and lt=80', { + gte: '30', + lte: '70', + gt: '20', + lt: '80' + }, data.slice(30, 71)) + + rangeTest('test iterator with gt=29 and lt=71', { + gt: '29', + lt: '71' + }, data.slice(30, 71)) + + rangeTest('test iterator with gte=30 and lte=70 and reverse=true', { + lte: '70', + gte: '30', + reverse: true + }, data.slice().reverse().slice(29, 70)) + + rangeTest('test iterator with gt=29 and lt=71 and reverse=true', { + lt: '71', + gt: '29', + reverse: true + }, data.slice().reverse().slice(29, 70)) + + rangeTest('test iterator with limit=20', { + limit: 20 + }, data.slice(0, 20)) + + rangeTest('test iterator with limit=20 and gte=20', { + limit: 20, + gte: '20' + }, data.slice(20, 40)) + + rangeTest('test iterator with limit=20 and reverse=true', { + limit: 20, + reverse: true + }, data.slice().reverse().slice(0, 20)) + + rangeTest('test iterator with limit=20 and lte=79 and reverse=true', { + limit: 20, + lte: '79', + reverse: true + }, data.slice().reverse().slice(20, 40)) + + // the default limit value from levelup is -1 + rangeTest('test iterator with limit=-1 should iterate over whole database', { + limit: -1 + }, data) + + rangeTest('test iterator with limit=0 should not iterate over anything', { + limit: 0 + }, []) + + rangeTest('test iterator with lte after limit', { + limit: 20, + lte: '50' + }, data.slice(0, 20)) + + rangeTest('test iterator with lte before limit', { + limit: 50, + lte: '19' + }, data.slice(0, 20)) + + rangeTest('test iterator with gte after database end', { + gte: '9a' + }, []) + + rangeTest('test iterator with gt after database end', { + gt: '9a' + }, []) + + rangeTest('test iterator with lte after database end and reverse=true', { + lte: '9a', + reverse: true + }, data.slice().reverse()) + + rangeTest('test iterator with lt after database end', { + lt: 'a' + }, data.slice()) + + rangeTest('test iterator with lt at database end', { + lt: data[data.length - 1].key + }, data.slice(0, -1)) + + rangeTest('test iterator with lte at database end', { + lte: data[data.length - 1].key + }, data.slice()) + + rangeTest('test iterator with lt before database end', { + lt: data[data.length - 2].key + }, data.slice(0, -2)) + + rangeTest('test iterator with lte before database end', { + lte: data[data.length - 2].key + }, data.slice(0, -1)) + + rangeTest('test iterator with lte and gte after database and reverse=true', { + lte: '9b', + gte: '9a', + reverse: true + }, []) + + rangeTest('test iterator with lt and gt after database and reverse=true', { + lt: '9b', + gt: '9a', + reverse: true + }, []) + + rangeTest('gt greater than lt', { + gt: '20', + lt: '10' + }, []) + + rangeTest('gte greater than lte', { + gte: '20', + lte: '10' + }, []) +} + +exports.tearDown = function (test, testCommon) { + test('tearDown', function (t) { + db.close(t.end.bind(t)) + }) +} + +exports.all = function (test, testCommon) { + exports.setUp(test, testCommon) + exports.range(test, testCommon) + exports.tearDown(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-seek-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-seek-test.js new file mode 100644 index 0000000..183e7f9 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-seek-test.js @@ -0,0 +1,252 @@ +'use strict' + +const { Buffer } = require('buffer') +const identity = (v) => v + +exports.all = function (test, testCommon) { + exports.sequence(test, testCommon) + exports.seek(test, testCommon) +} + +exports.sequence = function (test, testCommon) { + for (const deferred of [false, true]) { + for (const mode of ['iterator', 'keys', 'values']) { + test(`${mode}().seek() throws if next() has not completed (deferred: ${deferred})`, async function (t) { + const db = testCommon.factory() + if (!deferred) await db.open() + + const it = db[mode]() + const promise = it.next() + + t.throws(() => it.seek('two'), (err) => err.code === 'LEVEL_ITERATOR_BUSY') + + await promise + await db.close() + }) + + test(`${mode}().seek() does not throw after close() (deferred: ${deferred})`, async function (t) { + const db = testCommon.factory() + if (!deferred) await db.open() + + const it = db[mode]() + await it.close() + + t.doesNotThrow(() => it.seek('two')) + + await db.close() + }) + } + } +} + +exports.seek = function (test, testCommon) { + const testData = () => [ + { type: 'put', key: 'one', value: '1' }, + { type: 'put', key: 'two', value: '2' }, + { type: 'put', key: 'three', value: '3' } + ] + + for (const mode of ['iterator', 'keys', 'values']) { + const mapEntry = mode === 'iterator' ? e => e : mode === 'keys' ? e => e[0] : e => e[1] + + test(`${mode}().seek() to string target`, async function (t) { + const db = testCommon.factory() + await db.batch(testData()) + const it = db[mode]() + + it.seek('two') + + t.same(await it.next(), mapEntry(['two', '2']), 'match') + t.same(await it.next(), undefined, 'end of iterator') + + return db.close() + }) + + if (testCommon.supports.encodings.buffer) { + // TODO: make this test meaningful, with bytes outside the utf8 range + test(`${mode}().seek() to buffer target`, async function (t) { + const db = testCommon.factory() + await db.batch(testData()) + const it = db[mode]({ keyEncoding: 'buffer' }) + + it.seek(Buffer.from('two')) + + t.same(await it.next(), mapEntry([Buffer.from('two'), '2']), 'match') + t.same(await it.next(), undefined, 'end of iterator') + + return db.close() + }) + } + + test(`${mode}().seek() to target with custom encoding`, async function (t) { + const db = testCommon.factory() + await db.batch(testData()) + const it = db[mode]() + const keyEncoding = { encode: () => 'two', decode: identity, format: 'utf8' } + + it.seek('xyz', { keyEncoding }) + + t.same(await it.next(), mapEntry(['two', '2']), 'match') + t.same(await it.next(), undefined, 'end of iterator') + + return db.close() + }) + + test(`${mode}().seek() on reverse iterator`, async function (t) { + const db = testCommon.factory() + await db.batch(testData()) + const it = db[mode]({ reverse: true, limit: 1 }) + + // Should land on key equal to or smaller than 'three!' which is 'three' + it.seek('three!') + + t.same(await it.next(), mapEntry(['three', '3']), 'match') + t.same(await it.next(), undefined, 'end of iterator') + + return db.close() + }) + + test(`${mode}().seek() to out of range target`, async function (t) { + const db = testCommon.factory() + await db.batch(testData()) + const it = db[mode]() + + it.seek('zzz') + t.same(await it.next(), undefined, 'end of iterator') + + return db.close() + }) + + test(`${mode}().seek() on reverse iterator to out of range target`, async function (t) { + const db = testCommon.factory() + await db.batch(testData()) + const it = db[mode]({ reverse: true }) + + it.seek('zzz') + + t.same(await it.next(), mapEntry(['two', '2']), 'match') + t.same(await it.next(), mapEntry(['three', '3']), 'match') + t.same(await it.next(), mapEntry(['one', '1']), 'match') + t.same(await it.next(), undefined, 'end of iterator') + + return db.close() + }) + + if (testCommon.supports.snapshots) { + for (const reverse of [false, true]) { + for (const deferred of [false, true]) { + test(`${mode}().seek() respects snapshot (reverse: ${reverse}, deferred: ${deferred})`, async function (t) { + const db = testCommon.factory() + if (!deferred) await db.open() + + const it = db[mode]({ reverse }) + + // Add entry after having created the iterator (and its snapshot) + await db.put('a', 'a') + + // Seeking should not create a new snapshot, which'd include the new entry + it.seek('a') + t.same(await it.next(), undefined) + + return db.close() + }) + } + } + } + + test(`${mode}().seek() respects range`, function (t) { + const db = testCommon.factory() + + db.open(function (err) { + t.error(err, 'no error from open()') + + // Can't use Array.fill() because IE + const ops = [] + + for (let i = 0; i < 10; i++) { + ops.push({ type: 'put', key: String(i), value: String(i) }) + } + + db.batch(ops, function (err) { + t.error(err, 'no error from batch()') + + let pending = 0 + + expect({ gt: '5' }, '4', undefined) + expect({ gt: '5' }, '5', undefined) + expect({ gt: '5' }, '6', '6') + + expect({ gte: '5' }, '4', undefined) + expect({ gte: '5' }, '5', '5') + expect({ gte: '5' }, '6', '6') + + // The gte option should take precedence over gt. + expect({ gte: '5', gt: '7' }, '4', undefined) + expect({ gte: '5', gt: '7' }, '5', '5') + expect({ gte: '5', gt: '7' }, '6', '6') + expect({ gte: '5', gt: '3' }, '4', undefined) + expect({ gte: '5', gt: '3' }, '5', '5') + expect({ gte: '5', gt: '3' }, '6', '6') + + expect({ lt: '5' }, '4', '4') + expect({ lt: '5' }, '5', undefined) + expect({ lt: '5' }, '6', undefined) + + expect({ lte: '5' }, '4', '4') + expect({ lte: '5' }, '5', '5') + expect({ lte: '5' }, '6', undefined) + + // The lte option should take precedence over lt. + expect({ lte: '5', lt: '3' }, '4', '4') + expect({ lte: '5', lt: '3' }, '5', '5') + expect({ lte: '5', lt: '3' }, '6', undefined) + expect({ lte: '5', lt: '7' }, '4', '4') + expect({ lte: '5', lt: '7' }, '5', '5') + expect({ lte: '5', lt: '7' }, '6', undefined) + + expect({ lt: '5', reverse: true }, '4', '4') + expect({ lt: '5', reverse: true }, '5', undefined) + expect({ lt: '5', reverse: true }, '6', undefined) + + expect({ lte: '5', reverse: true }, '4', '4') + expect({ lte: '5', reverse: true }, '5', '5') + expect({ lte: '5', reverse: true }, '6', undefined) + + expect({ gt: '5', reverse: true }, '4', undefined) + expect({ gt: '5', reverse: true }, '5', undefined) + expect({ gt: '5', reverse: true }, '6', '6') + + expect({ gte: '5', reverse: true }, '4', undefined) + expect({ gte: '5', reverse: true }, '5', '5') + expect({ gte: '5', reverse: true }, '6', '6') + + expect({ gt: '7', lt: '8' }, '7', undefined) + expect({ gte: '7', lt: '8' }, '7', '7') + expect({ gte: '7', lt: '8' }, '8', undefined) + expect({ gt: '7', lte: '8' }, '8', '8') + + function expect (range, target, expected) { + pending++ + const ite = db[mode](range) + + ite.seek(target) + ite.next(function (err, item) { + t.error(err, 'no error from next()') + + const json = JSON.stringify(range) + const msg = 'seek(' + target + ') on ' + json + ' yields ' + expected + + // Either a key or value depending on mode + t.is(item, expected, msg) + + ite.close(function (err) { + t.error(err, 'no error from close()') + if (!--pending) db.close(t.end.bind(t)) + }) + }) + } + }) + }) + }) + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-snapshot-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-snapshot-test.js new file mode 100644 index 0000000..3642e05 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-snapshot-test.js @@ -0,0 +1,99 @@ +'use strict' + +exports.snapshot = function (test, testCommon) { + function make (run) { + return function (t) { + const db = testCommon.factory() + + db.open(function (err) { + t.ifError(err, 'no open error') + + db.put('z', 'from snapshot', function (err) { + t.ifError(err, 'no put error') + + // For this test it is important that we don't read eagerly. + // NOTE: highWaterMarkBytes is not an abstract option, but + // it is supported by classic-level and others. Also set the + // old & equivalent leveldown highWaterMark option for compat. + const it = db.iterator({ highWaterMarkBytes: 0, highWaterMark: 0 }) + + run(t, db, it, function end (err) { + t.ifError(err, 'no run error') + + it.close(function (err) { + t.ifError(err, 'no iterator close error') + db.close(t.end.bind(t)) + }) + }) + }) + }) + } + } + + test('delete key after snapshotting', make(function (t, db, it, end) { + db.del('z', function (err) { + t.ifError(err, 'no del error') + + it.next(function (err, key, value) { + t.ifError(err, 'no next error') + t.ok(key, 'got a key') + t.is(key.toString(), 'z', 'correct key') + t.is(value.toString(), 'from snapshot', 'correct value') + + end() + }) + }) + })) + + test('overwrite key after snapshotting', make(function (t, db, it, end) { + db.put('z', 'not from snapshot', function (err) { + t.ifError(err, 'no put error') + + it.next(function (err, key, value) { + t.ifError(err, 'no next error') + t.ok(key, 'got a key') + t.is(key.toString(), 'z', 'correct key') + t.is(value.toString(), 'from snapshot', 'correct value') + + end() + }) + }) + })) + + test('add key after snapshotting that sorts first', make(function (t, db, it, end) { + db.put('a', 'not from snapshot', function (err) { + t.ifError(err, 'no put error') + + it.next(function (err, key, value) { + t.ifError(err, 'no next error') + + t.ok(key, 'got a key') + t.is(key.toString(), 'z', 'correct key') + t.is(value.toString(), 'from snapshot', 'correct value') + + end() + }) + }) + })) + + // NOTE: adapted from memdown + test('delete key after snapshotting, with more entries available', async function (t) { + const db = testCommon.factory() + await db.open() + await Promise.all([db.put('a', 'A'), db.put('b', 'B'), db.put('c', 'C')]) + + const iterator = db.iterator({ gte: 'a' }) + t.same(await iterator.next(), ['a', 'A']) + + await db.del('b') + t.same(await iterator.next(), ['b', 'B']) + t.same(await iterator.next(), ['c', 'C']) + + await iterator.close() + return db.close() + }) +} + +exports.all = function (test, testCommon) { + exports.snapshot(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-test.js new file mode 100644 index 0000000..6faa1ea --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/iterator-test.js @@ -0,0 +1,548 @@ +'use strict' + +const { Buffer } = require('buffer') +const identity = (v) => v + +let db + +exports.setUp = function (test, testCommon) { + test('setUp db', function (t) { + db = testCommon.factory() + db.open(t.end.bind(t)) + }) +} + +exports.args = function (test, testCommon) { + for (const mode of ['iterator', 'keys', 'values']) { + test(`${mode}() has db reference`, async function (t) { + const it = db[mode]() + + // May return iterator of an underlying db, that's okay. + t.ok(it.db === db || it.db === (db.db || db._db || db)) + + await it.close() + }) + + test(`${mode}() has limit and count properties`, async function (t) { + const iterators = [db[mode]()] + t.is(iterators[0].limit, Infinity, 'defaults to infinite') + + for (const limit of [-1, 0, 1, Infinity]) { + const it = db[mode]({ limit }) + iterators.push(it) + t.is(it.limit, limit === -1 ? Infinity : limit, 'has limit property') + } + + t.ok(iterators.every(it => it.count === 0), 'has count property') + await Promise.all(iterators.map(it => it.close())) + }) + + test(`${mode}().nextv() yields error if size is invalid`, async function (t) { + t.plan(4) + + const it = db[mode]() + + for (const args of [[], [NaN], ['1'], [2.5]]) { + try { + await it.nextv(...args) + } catch (err) { + t.is(err.message, "The first argument 'size' must be an integer") + } + } + + await it.close() + }) + } +} + +exports.sequence = function (test, testCommon) { + for (const mode of ['iterator', 'keys', 'values']) { + test(`${mode}().close() is idempotent`, function (t) { + const iterator = db[mode]() + + iterator.close(function () { + let async = false + + iterator.close(function () { + t.ok(async, 'callback is asynchronous') + t.end() + }) + + async = true + }) + }) + + for (const method of ['next', 'nextv', 'all']) { + const requiredArgs = method === 'nextv' ? [1] : [] + + test(`${mode}().${method}() after close() yields error`, function (t) { + const iterator = db[mode]() + iterator.close(function (err) { + t.error(err) + + let async = false + + iterator[method](...requiredArgs, function (err2) { + t.ok(err2, 'returned error') + t.is(err2.code, 'LEVEL_ITERATOR_NOT_OPEN', 'correct message') + t.ok(async, 'callback is asynchronous') + t.end() + }) + + async = true + }) + }) + + for (const otherMethod of ['next', 'nextv', 'all']) { + const otherRequiredArgs = otherMethod === 'nextv' ? [1] : [] + + test(`${mode}().${method}() while busy with ${otherMethod}() yields error`, function (t) { + const iterator = db[mode]() + iterator[otherMethod](...otherRequiredArgs, function (err) { + t.error(err) + iterator.close(function (err) { + t.error(err) + t.end() + }) + }) + + let async = false + + iterator[method](...requiredArgs, function (err) { + t.ok(err, 'returned error') + t.is(err.code, 'LEVEL_ITERATOR_BUSY') + t.ok(async, 'callback is asynchronous') + }) + + async = true + }) + } + } + } + + for (const deferred of [false, true]) { + for (const mode of ['iterator', 'keys', 'values']) { + for (const method of ['next', 'nextv', 'all']) { + const requiredArgs = method === 'nextv' ? [10] : [] + + // NOTE: adapted from leveldown + test(`${mode}().${method}() after db.close() yields error (deferred: ${deferred})`, async function (t) { + t.plan(2) + + const db = testCommon.factory() + if (!deferred) await db.open() + + await db.put('a', 'a') + await db.put('b', 'b') + + const it = db[mode]() + + // The first call *should* succeed, because it was scheduled before close(). However, success + // is not a must. Because nextv() and all() fallback to next*(), they're allowed to fail. An + // implementation can also choose to abort any pending call on close. + let promise = it[method](...requiredArgs).then(() => { + t.pass('Optionally succeeded') + }).catch((err) => { + t.is(err.code, 'LEVEL_ITERATOR_NOT_OPEN') + }) + + // The second call *must* fail, because it was scheduled after close() + promise = promise.then(() => { + return it[method](...requiredArgs).then(() => { + t.fail('Expected an error') + }).catch((err) => { + t.is(err.code, 'LEVEL_ITERATOR_NOT_OPEN') + }) + }) + + return Promise.all([db.close(), promise]) + }) + } + } + } +} + +exports.iterator = function (test, testCommon) { + test('test simple iterator()', function (t) { + const data = [ + { type: 'put', key: 'foobatch1', value: 'bar1' }, + { type: 'put', key: 'foobatch2', value: 'bar2' }, + { type: 'put', key: 'foobatch3', value: 'bar3' } + ] + let idx = 0 + + db.batch(data, function (err) { + t.error(err) + const iterator = db.iterator() + const fn = function (err, key, value) { + t.error(err) + if (key && value) { + t.is(key, data[idx].key, 'correct key') + t.is(value, data[idx].value, 'correct value') + db.nextTick(next) + idx++ + } else { // end + t.ok(err == null, 'err argument is nullish') + t.ok(typeof key === 'undefined', 'key argument is undefined') + t.ok(typeof value === 'undefined', 'value argument is undefined') + t.is(idx, data.length, 'correct number of entries') + iterator.close(function () { + t.end() + }) + } + } + const next = function () { + iterator.next(fn) + } + + next() + }) + }) + + // NOTE: adapted from leveldown + test('key-only iterator', function (t) { + const it = db.iterator({ values: false }) + + it.next(function (err, key, value) { + t.ifError(err, 'no next() error') + t.is(key, 'foobatch1') + t.is(value, undefined) + it.close(t.end.bind(t)) + }) + }) + + // NOTE: adapted from leveldown + test('value-only iterator', function (t) { + const it = db.iterator({ keys: false }) + + it.next(function (err, key, value) { + t.ifError(err, 'no next() error') + t.is(key, undefined) + t.is(value, 'bar1') + it.close(t.end.bind(t)) + }) + }) + + test('db.keys().next()', function (t) { + const it = db.keys() + + it.next(function (err, key) { + t.ifError(err, 'no next() error') + t.is(key, 'foobatch1') + it.close(t.end.bind(t)) + }) + }) + + test('db.values().next()', function (t) { + const it = db.values() + + it.next(function (err, value) { + t.ifError(err, 'no next() error') + t.is(value, 'bar1') + it.close(t.end.bind(t)) + }) + }) + + for (const mode of ['iterator', 'keys', 'values']) { + const mapEntry = e => mode === 'iterator' ? e : mode === 'keys' ? e[0] : e[1] + + test(`${mode}().nextv()`, async function (t) { + const it = db[mode]() + + t.same(await it.nextv(1), [['foobatch1', 'bar1']].map(mapEntry)) + t.same(await it.nextv(2, {}), [['foobatch2', 'bar2'], ['foobatch3', 'bar3']].map(mapEntry)) + t.same(await it.nextv(2), []) + + await it.close() + }) + + test(`${mode}().nextv() in reverse`, async function (t) { + const it = db[mode]({ reverse: true }) + + t.same(await it.nextv(1), [['foobatch3', 'bar3']].map(mapEntry)) + t.same(await it.nextv(2, {}), [['foobatch2', 'bar2'], ['foobatch1', 'bar1']].map(mapEntry)) + t.same(await it.nextv(2), []) + + await it.close() + }) + + test(`${mode}().nextv() has soft minimum of 1`, async function (t) { + const it = db[mode]() + + t.same(await it.nextv(0), [['foobatch1', 'bar1']].map(mapEntry)) + t.same(await it.nextv(0), [['foobatch2', 'bar2']].map(mapEntry)) + t.same(await it.nextv(0, {}), [['foobatch3', 'bar3']].map(mapEntry)) + t.same(await it.nextv(0), []) + + await it.close() + }) + + test(`${mode}().nextv() requesting more than available`, async function (t) { + const it = db[mode]() + + t.same(await it.nextv(10), [ + ['foobatch1', 'bar1'], + ['foobatch2', 'bar2'], + ['foobatch3', 'bar3'] + ].map(mapEntry)) + t.same(await it.nextv(10), []) + + await it.close() + }) + + test(`${mode}().nextv() honors limit`, async function (t) { + const it = db[mode]({ limit: 2 }) + + t.same(await it.nextv(10), [['foobatch1', 'bar1'], ['foobatch2', 'bar2']].map(mapEntry)) + t.same(await it.nextv(10), []) + + await it.close() + }) + + test(`${mode}().nextv() honors limit in reverse`, async function (t) { + const it = db[mode]({ limit: 2, reverse: true }) + + t.same(await it.nextv(10), [['foobatch3', 'bar3'], ['foobatch2', 'bar2']].map(mapEntry)) + t.same(await it.nextv(10), []) + + await it.close() + }) + + test(`${mode}().all()`, async function (t) { + t.same(await db[mode]().all(), [ + ['foobatch1', 'bar1'], + ['foobatch2', 'bar2'], + ['foobatch3', 'bar3'] + ].map(mapEntry)) + + t.same(await db[mode]().all({}), [ + ['foobatch1', 'bar1'], + ['foobatch2', 'bar2'], + ['foobatch3', 'bar3'] + ].map(mapEntry)) + }) + + test(`${mode}().all() in reverse`, async function (t) { + t.same(await db[mode]({ reverse: true }).all(), [ + ['foobatch3', 'bar3'], + ['foobatch2', 'bar2'], + ['foobatch1', 'bar1'] + ].map(mapEntry)) + }) + + test(`${mode}().all() honors limit`, async function (t) { + t.same(await db[mode]({ limit: 2 }).all(), [ + ['foobatch1', 'bar1'], + ['foobatch2', 'bar2'] + ].map(mapEntry)) + + const it = db[mode]({ limit: 2 }) + + t.same(await it.next(), mapEntry(['foobatch1', 'bar1'])) + t.same(await it.all(), [['foobatch2', 'bar2']].map(mapEntry)) + }) + + test(`${mode}().all() honors limit in reverse`, async function (t) { + t.same(await db[mode]({ limit: 2, reverse: true }).all(), [ + ['foobatch3', 'bar3'], + ['foobatch2', 'bar2'] + ].map(mapEntry)) + + const it = db[mode]({ limit: 2, reverse: true }) + + t.same(await it.next(), mapEntry(['foobatch3', 'bar3'])) + t.same(await it.all(), [['foobatch2', 'bar2']].map(mapEntry)) + }) + } + + // NOTE: adapted from memdown + test('iterator() sorts lexicographically', async function (t) { + const db = testCommon.factory() + await db.open() + + // Write in unsorted order with multiple operations + await db.put('f', 'F') + await db.put('a', 'A') + await db.put('~', '~') + await db.put('e', 'E') + await db.put('🐄', '🐄') + await db.batch([ + { type: 'put', key: 'd', value: 'D' }, + { type: 'put', key: 'b', value: 'B' }, + { type: 'put', key: 'ff', value: 'FF' }, + { type: 'put', key: 'a🐄', value: 'A🐄' } + ]) + await db.batch([ + { type: 'put', key: '', value: 'empty' }, + { type: 'put', key: '2', value: '2' }, + { type: 'put', key: '12', value: '12' }, + { type: 'put', key: '\t', value: '\t' } + ]) + + t.same(await db.iterator().all(), [ + ['', 'empty'], + ['\t', '\t'], + ['12', '12'], + ['2', '2'], + ['a', 'A'], + ['a🐄', 'A🐄'], + ['b', 'B'], + ['d', 'D'], + ['e', 'E'], + ['f', 'F'], + ['ff', 'FF'], + ['~', '~'], + ['🐄', '🐄'] + ]) + + t.same(await db.iterator({ lte: '' }).all(), [ + ['', 'empty'] + ]) + + return db.close() + }) + + for (const keyEncoding of ['buffer', 'view']) { + if (!testCommon.supports.encodings[keyEncoding]) continue + + test(`test iterator() has byte order (${keyEncoding} encoding)`, function (t) { + const db = testCommon.factory({ keyEncoding }) + + db.open(function (err) { + t.ifError(err, 'no open() error') + + const ctor = keyEncoding === 'buffer' ? Buffer : Uint8Array + const keys = [2, 11, 1].map(b => ctor.from([b])) + + db.batch(keys.map((key) => ({ type: 'put', key, value: 'x' })), function (err) { + t.ifError(err, 'no batch() error') + + db.keys().all(function (err, keys) { + t.ifError(err, 'no all() error') + t.same(keys.map(k => k[0]), [1, 2, 11], 'order is ok') + + db.iterator().all(function (err, entries) { + t.ifError(err, 'no all() error') + t.same(entries.map(e => e[0][0]), [1, 2, 11], 'order is ok') + + db.close(t.end.bind(t)) + }) + }) + }) + }) + }) + + // NOTE: adapted from memdown and level-js + test(`test iterator() with byte range (${keyEncoding} encoding)`, async function (t) { + const db = testCommon.factory({ keyEncoding }) + await db.open() + + await db.put(Uint8Array.from([0x0]), '0') + await db.put(Uint8Array.from([128]), '128') + await db.put(Uint8Array.from([160]), '160') + await db.put(Uint8Array.from([192]), '192') + + const collect = async (range) => { + const entries = await db.iterator(range).all() + t.ok(entries.every(e => e[0] instanceof Uint8Array)) // True for both encodings + t.ok(entries.every(e => e[1] === String(e[0][0]))) + return entries.map(e => e[0][0]) + } + + t.same(await collect({ gt: Uint8Array.from([255]) }), []) + t.same(await collect({ gt: Uint8Array.from([192]) }), []) + t.same(await collect({ gt: Uint8Array.from([160]) }), [192]) + t.same(await collect({ gt: Uint8Array.from([128]) }), [160, 192]) + t.same(await collect({ gt: Uint8Array.from([0x0]) }), [128, 160, 192]) + t.same(await collect({ gt: Uint8Array.from([]) }), [0x0, 128, 160, 192]) + + t.same(await collect({ lt: Uint8Array.from([255]) }), [0x0, 128, 160, 192]) + t.same(await collect({ lt: Uint8Array.from([192]) }), [0x0, 128, 160]) + t.same(await collect({ lt: Uint8Array.from([160]) }), [0x0, 128]) + t.same(await collect({ lt: Uint8Array.from([128]) }), [0x0]) + t.same(await collect({ lt: Uint8Array.from([0x0]) }), []) + t.same(await collect({ lt: Uint8Array.from([]) }), []) + + t.same(await collect({ gte: Uint8Array.from([255]) }), []) + t.same(await collect({ gte: Uint8Array.from([192]) }), [192]) + t.same(await collect({ gte: Uint8Array.from([160]) }), [160, 192]) + t.same(await collect({ gte: Uint8Array.from([128]) }), [128, 160, 192]) + t.same(await collect({ gte: Uint8Array.from([0x0]) }), [0x0, 128, 160, 192]) + t.same(await collect({ gte: Uint8Array.from([]) }), [0x0, 128, 160, 192]) + + t.same(await collect({ lte: Uint8Array.from([255]) }), [0x0, 128, 160, 192]) + t.same(await collect({ lte: Uint8Array.from([192]) }), [0x0, 128, 160, 192]) + t.same(await collect({ lte: Uint8Array.from([160]) }), [0x0, 128, 160]) + t.same(await collect({ lte: Uint8Array.from([128]) }), [0x0, 128]) + t.same(await collect({ lte: Uint8Array.from([0x0]) }), [0x0]) + t.same(await collect({ lte: Uint8Array.from([]) }), []) + + return db.close() + }) + } +} + +exports.decode = function (test, testCommon) { + for (const deferred of [false, true]) { + for (const mode of ['iterator', 'keys', 'values']) { + for (const method of ['next', 'nextv', 'all']) { + const requiredArgs = method === 'nextv' ? [1] : [] + + for (const encodingOption of ['keyEncoding', 'valueEncoding']) { + if (mode === 'keys' && encodingOption === 'valueEncoding') continue + if (mode === 'values' && encodingOption === 'keyEncoding') continue + + // NOTE: adapted from encoding-down + test(`${mode}().${method}() catches decoding error from ${encodingOption} (deferred: ${deferred})`, async function (t) { + t.plan(4) + + const encoding = { + format: 'utf8', + decode: function (x) { + t.is(x, encodingOption === 'keyEncoding' ? 'testKey' : 'testValue') + throw new Error('from encoding') + }, + encode: identity + } + + const db = testCommon.factory() + await db.put('testKey', 'testValue') + + if (deferred) { + await db.close() + db.open(t.ifError.bind(t)) + } else { + t.pass('non-deferred') + } + + const it = db[mode]({ [encodingOption]: encoding }) + + try { + await it[method](...requiredArgs) + } catch (err) { + t.is(err.code, 'LEVEL_DECODE_ERROR') + t.is(err.cause && err.cause.message, 'from encoding') + } + + return db.close() + }) + } + } + } + } +} + +exports.tearDown = function (test, testCommon) { + test('tearDown', function (t) { + db.close(t.end.bind(t)) + }) +} + +exports.all = function (test, testCommon) { + exports.setUp(test, testCommon) + exports.args(test, testCommon) + exports.sequence(test, testCommon) + exports.iterator(test, testCommon) + exports.decode(test, testCommon) + exports.tearDown(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/manifest-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/manifest-test.js new file mode 100644 index 0000000..519271b --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/manifest-test.js @@ -0,0 +1,24 @@ +'use strict' + +const suite = require('level-supports/test') + +module.exports = function (test, testCommon) { + suite(test, testCommon) + + test('manifest has expected properties', function (t) { + const db = testCommon.factory() + + t.is(db.supports.status, true) + t.is(db.supports.promises, true) + t.is(db.supports.clear, true) + t.is(db.supports.getMany, true) + + testCommon.supports = db.supports + t.ok(testCommon.supports, 'can be accessed via testCommon') + + t.ok(db.supports.encodings.utf8, 'supports utf8') + t.ok(db.supports.encodings.json, 'supports json') + + db.close(t.end.bind(t)) + }) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/open-create-if-missing-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/open-create-if-missing-test.js new file mode 100644 index 0000000..3720c82 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/open-create-if-missing-test.js @@ -0,0 +1,35 @@ +'use strict' + +exports.createIfMissing = function (test, testCommon) { + test('test database open createIfMissing:false', function (t) { + const db = testCommon.factory() + let async = false + + db.open({ createIfMissing: false }, function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + t.ok(err && /does not exist/.test(err.cause && err.cause.message), 'error is about dir not existing') + t.ok(async, 'callback is asynchronous') + t.end() + }) + + async = true + }) + + test('test database open createIfMissing:false via constructor', function (t) { + const db = testCommon.factory({ createIfMissing: false }) + let async = false + + db.open(function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + t.ok(err && /does not exist/.test(err.cause && err.cause.message), 'error is about dir not existing') + t.ok(async, 'callback is asynchronous') + t.end() + }) + + async = true + }) +} + +exports.all = function (test, testCommon) { + exports.createIfMissing(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/open-error-if-exists-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/open-error-if-exists-test.js new file mode 100644 index 0000000..50e530f --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/open-error-if-exists-test.js @@ -0,0 +1,29 @@ +'use strict' + +exports.errorIfExists = function (test, testCommon) { + test('test database open errorIfExists:true', function (t) { + const db = testCommon.factory() + + db.open(function (err) { + t.error(err) + db.close(function (err) { + t.error(err) + + let async = false + + db.open({ createIfMissing: false, errorIfExists: true }, function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + t.ok(err && /exists/.test(err.cause && err.cause.message), 'error is about already existing') + t.ok(async, 'callback is asynchronous') + t.end() + }) + + async = true + }) + }) + }) +} + +exports.all = function (test, testCommon) { + exports.errorIfExists(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/open-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/open-test.js new file mode 100644 index 0000000..b29fcba --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/open-test.js @@ -0,0 +1,297 @@ +'use strict' + +const { assertAsync } = require('./util') + +exports.open = function (test, testCommon) { + test('test database open, no options', function (t) { + const db = testCommon.factory() + + t.is(db.status, 'opening') + + // default createIfMissing=true, errorIfExists=false + db.open(function (err) { + t.error(err) + t.is(db.status, 'open') + + db.close(function () { + t.is(db.status, 'closed') + t.end() + }) + }) + + t.is(db.status, 'opening') + }) + + test('test database open, no options, with promise', function (t) { + const db = testCommon.factory() + + t.is(db.status, 'opening') + + // default createIfMissing=true, errorIfExists=false + db.open().then(function () { + t.is(db.status, 'open') + db.close(t.end.bind(t)) + }).catch(t.fail.bind(t)) + + t.is(db.status, 'opening') + }) + + test('test database open, options and callback', function (t) { + const db = testCommon.factory() + + // default createIfMissing=true, errorIfExists=false + db.open({}, function (err) { + t.error(err) + db.close(function () { + t.end() + }) + }) + }) + + test('test database open, options with promise', function (t) { + const db = testCommon.factory() + + // default createIfMissing=true, errorIfExists=false + db.open({}).then(function () { + db.close(t.end.bind(t)) + }) + }) + + test('test database open, close and open', function (t) { + const db = testCommon.factory() + + db.open(function (err) { + t.error(err) + + db.close(function (err) { + t.error(err) + t.is(db.status, 'closed') + + db.open(function (err) { + t.error(err) + t.is(db.status, 'open') + + db.close(t.end.bind(t)) + }) + }) + }) + }) + + test('test database open, close and open with promise', function (t) { + const db = testCommon.factory() + + db.open().then(function () { + db.close(function (err) { + t.error(err) + db.open().then(function () { + db.close(function () { + t.end() + }) + }).catch(t.fail.bind(t)) + }) + }).catch(t.fail.bind(t)) + }) + + test('test database open and close in same tick', assertAsync.ctx(function (t) { + t.plan(10) + + const db = testCommon.factory() + const order = [] + + db.open(assertAsync(function (err) { + order.push('A') + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN', 'got open() error') + t.is(db.status, 'closed', 'is closed') + })) + + t.is(db.status, 'opening', 'is opening') + + // This wins from the open() call + db.close(assertAsync(function (err) { + order.push('B') + t.same(order, ['A', 'closed event', 'B'], 'order is correct') + t.ifError(err, 'no close() error') + t.is(db.status, 'closed', 'is closed') + })) + + // But open() is still in control + t.is(db.status, 'opening', 'is still opening') + + // Should not emit 'open', because close() wins + db.on('open', t.fail.bind(t)) + db.on('closed', assertAsync(() => { order.push('closed event') })) + })) + + test('test database open, close and open in same tick', assertAsync.ctx(function (t) { + t.plan(14) + + const db = testCommon.factory() + const order = [] + + db.open(assertAsync(function (err) { + order.push('A') + t.ifError(err, 'no open() error (1)') + t.is(db.status, 'open', 'is open') + })) + + t.is(db.status, 'opening', 'is opening') + + // This wins from the open() call + db.close(assertAsync(function (err) { + order.push('B') + t.is(err && err.code, 'LEVEL_DATABASE_NOT_CLOSED') + t.is(db.status, 'open', 'is open') + })) + + t.is(db.status, 'opening', 'is still opening') + + // This wins from the close() call + db.open(assertAsync(function (err) { + order.push('C') + t.same(order, ['A', 'B', 'open event', 'C'], 'callback order is the same as call order') + t.ifError(err, 'no open() error (2)') + t.is(db.status, 'open', 'is open') + })) + + // Should not emit 'closed', because open() wins + db.on('closed', t.fail.bind(t)) + db.on('open', assertAsync(() => { order.push('open event') })) + + t.is(db.status, 'opening', 'is still opening') + })) + + test('test database open if already open (sequential)', function (t) { + t.plan(7) + + const db = testCommon.factory() + + db.open(assertAsync(function (err) { + t.ifError(err, 'no open() error (1)') + t.is(db.status, 'open', 'is open') + + db.open(assertAsync(function (err) { + t.ifError(err, 'no open() error (2)') + t.is(db.status, 'open', 'is open') + })) + + t.is(db.status, 'open', 'not reopening') + db.on('open', t.fail.bind(t)) + assertAsync.end(t) + })) + + assertAsync.end(t) + }) + + test('test database open if already opening (parallel)', assertAsync.ctx(function (t) { + t.plan(7) + + const db = testCommon.factory() + + db.open(assertAsync(function (err) { + t.ifError(err, 'no open() error (1)') + t.is(db.status, 'open') + })) + + db.open(assertAsync(function (err) { + t.ifError(err, 'no open() error (2)') + t.is(db.status, 'open') + db.close(t.end.bind(t)) + })) + + t.is(db.status, 'opening') + })) + + test('test database close if already closed', function (t) { + t.plan(8) + + const db = testCommon.factory() + + db.open(function (err) { + t.ifError(err, 'no open() error') + + db.close(assertAsync(function (err) { + t.ifError(err, 'no close() error (1)') + t.is(db.status, 'closed', 'is closed') + + db.close(assertAsync(function (err) { + t.ifError(err, 'no close() error (2)') + t.is(db.status, 'closed', 'is closed') + })) + + t.is(db.status, 'closed', 'is closed', 'not reclosing') + db.on('closed', t.fail.bind(t)) + assertAsync.end(t) + })) + + assertAsync.end(t) + }) + }) + + test('test database close if new', assertAsync.ctx(function (t) { + t.plan(5) + + const db = testCommon.factory() + const expectedStatus = db.supports.deferredOpen ? 'opening' : 'closed' + + t.is(db.status, expectedStatus, 'status ok') + + db.close(assertAsync(function (err) { + t.ifError(err, 'no close() error') + t.is(db.status, 'closed', 'status ok') + })) + + t.is(db.status, expectedStatus, 'status unchanged') + + if (!db.supports.deferredOpen) { + db.on('closed', t.fail.bind(t, 'should not emit closed')) + } + })) + + test('test database close on open event', function (t) { + t.plan(5) + + const db = testCommon.factory() + const order = [] + + db.open(function (err) { + order.push('A') + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN', 'got open() error') + t.is(db.status, 'closed', 'is closed') + }) + + db.on('open', function () { + // This wins from the (still in progress) open() call + db.close(function (err) { + order.push('B') + t.same(order, ['A', 'closed event', 'B'], 'order is correct') + t.ifError(err, 'no close() error') + t.is(db.status, 'closed', 'is closed') + }) + }) + + db.on('closed', () => { order.push('closed event') }) + }) + + test('test passive open()', async function (t) { + t.plan(1) + const db = testCommon.factory() + await db.open({ passive: true }) // OK, already opening + await db.close() + await db.open({ passive: true }).catch(err => { + t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN') + }) + await db.open() + await db.open({ passive: true }) // OK, already open + return db.close() + }) + + test('test passive open(): ignored if set in constructor options', async function (t) { + const db = testCommon.factory({ passive: true }) + await new Promise((resolve) => db.once('open', resolve)) + return db.close() + }) +} + +exports.all = function (test, testCommon) { + exports.open(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/put-get-del-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/put-get-del-test.js new file mode 100644 index 0000000..37da10a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/put-get-del-test.js @@ -0,0 +1,113 @@ +'use strict' + +const { verifyNotFoundError } = require('./util') +const { Buffer } = require('buffer') + +let db + +function makeTest (test, type, key, value, expectedResult) { + const hasExpectedResult = arguments.length === 5 + test('test put()/get()/del() with ' + type, function (t) { + db.put(key, value, function (err) { + t.error(err) + db.get(key, function (err, _value) { + t.error(err, 'no error, has key/value for `' + type + '`') + + let result = _value + + if (hasExpectedResult) { + t.equal(result.toString(), expectedResult) + } else { + if (result != null) { result = _value.toString() } + if (value != null) { value = value.toString() } + t.equals(result, value) + } + db.del(key, function (err) { + t.error(err, 'no error, deleted key/value for `' + type + '`') + + let async = false + + db.get(key, function (err, value) { + t.ok(err, 'entry properly deleted') + t.ok(verifyNotFoundError(err), 'correct error') + t.is(value, undefined, 'value is undefined') + t.ok(async, 'callback is asynchronous') + t.end() + }) + + async = true + }) + }) + }) + }) +} + +exports.setUp = function (test, testCommon) { + test('setUp db', function (t) { + db = testCommon.factory() + db.open(t.end.bind(t)) + }) +} + +exports.nonErrorKeys = function (test, testCommon) { + // valid falsey keys + makeTest(test, '`0` key', 0, 'foo 0') + makeTest(test, 'empty string key', 0, 'foo') + + // standard String key + makeTest( + test + , 'long String key' + , 'some long string that I\'m using as a key for this unit test, cross your fingers human, we\'re going in!' + , 'foo' + ) + + if (testCommon.supports.encodings.buffer) { + makeTest(test, 'Buffer key', Buffer.from('0080c0ff', 'hex'), 'foo') + makeTest(test, 'empty Buffer key', Buffer.alloc(0), 'foo') + } + + // non-empty Array as a value + makeTest(test, 'Array value', 'foo', [1, 2, 3, 4]) +} + +exports.nonErrorValues = function (test, testCommon) { + // valid falsey values + makeTest(test, '`false` value', 'foo false', false) + makeTest(test, '`0` value', 'foo 0', 0) + makeTest(test, '`NaN` value', 'foo NaN', NaN) + + // all of the following result in an empty-string value: + makeTest(test, 'empty String value', 'foo', '', '') + makeTest(test, 'empty Buffer value', 'foo', Buffer.alloc(0), '') + makeTest(test, 'empty Array value', 'foo', [], '') + + // String value + makeTest( + test + , 'long String value' + , 'foo' + , 'some long string that I\'m using as a key for this unit test, cross your fingers human, we\'re going in!' + ) + + // Buffer value + if (testCommon.supports.encodings.buffer) { + makeTest(test, 'Buffer value', 'foo', Buffer.from('0080c0ff', 'hex')) + } + + // non-empty Array as a key + makeTest(test, 'Array key', [1, 2, 3, 4], 'foo') +} + +exports.tearDown = function (test, testCommon) { + test('tearDown', function (t) { + db.close(t.end.bind(t)) + }) +} + +exports.all = function (test, testCommon) { + exports.setUp(test, testCommon) + exports.nonErrorKeys(test, testCommon) + exports.nonErrorValues(test, testCommon) + exports.tearDown(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/put-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/put-test.js new file mode 100644 index 0000000..c4f2bef --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/put-test.js @@ -0,0 +1,131 @@ +'use strict' + +const { assertAsync, illegalKeys, illegalValues } = require('./util') + +let db + +exports.setUp = function (test, testCommon) { + test('setUp db', function (t) { + db = testCommon.factory() + db.open(t.end.bind(t)) + }) +} + +exports.args = function (test, testCommon) { + test('test put() with illegal keys', assertAsync.ctx(function (t) { + t.plan(illegalKeys.length * 5) + + for (const { name, key } of illegalKeys) { + db.put(key, 'value', assertAsync(function (err) { + t.ok(err instanceof Error, name + ' - is Error (callback)') + t.is(err && err.code, 'LEVEL_INVALID_KEY', name + ' - correct error code (callback)') + })) + + db.put(key, 'value').catch(function (err) { + t.ok(err instanceof Error, name + ' - is Error (promise)') + t.is(err.code, 'LEVEL_INVALID_KEY', name + ' - correct error code (promise)') + }) + } + })) + + test('test put() with illegal values', assertAsync.ctx(function (t) { + t.plan(illegalValues.length * 5) + + for (const { name, value } of illegalValues) { + db.put('key', value, assertAsync(function (err) { + t.ok(err instanceof Error, name + '- is Error (callback)') + t.is(err && err.code, 'LEVEL_INVALID_VALUE', name + ' - correct error code (callback)') + })) + + db.put('key', value).catch(function (err) { + t.ok(err instanceof Error, name + ' - is Error (promise)') + t.is(err.code, 'LEVEL_INVALID_VALUE', name + ' - correct error code (promise)') + }) + } + })) +} + +exports.put = function (test, testCommon) { + test('test simple put()', assertAsync.ctx(function (t) { + t.plan(7) + + db.put('foo', 'bar', assertAsync(function (err) { + t.ifError(err, 'no put() error') + + db.get('foo', function (err, value) { + t.ifError(err, 'no get() error') + t.is(value, 'bar') + + db.put('foo', 'new', function (err) { + t.ifError(err, 'no put() error') + + db.get('foo', function (err, value) { + t.ifError(err, 'no get() error') + t.is(value, 'new', 'value was overwritten') + }) + }) + }) + })) + })) + + test('test simple put() with promise', async function (t) { + await db.put('foo2', 'bar') + t.is(await db.get('foo2'), 'bar') + }) + + test('test deferred put()', assertAsync.ctx(function (t) { + t.plan(5) + + const db = testCommon.factory() + + db.put('foo', 'bar', assertAsync(function (err) { + t.ifError(err, 'no put() error') + + db.get('foo', { valueEncoding: 'utf8' }, function (err, value) { + t.ifError(err, 'no get() error') + t.is(value, 'bar', 'value is ok') + db.close(t.ifError.bind(t)) + }) + })) + })) + + test('test deferred put() with promise', async function (t) { + const db = testCommon.factory() + await db.put('foo', 'bar') + t.is(await db.get('foo', { valueEncoding: 'utf8' }), 'bar', 'value is ok') + return db.close() + }) +} + +exports.events = function (test, testCommon) { + test('test put() emits put event', async function (t) { + t.plan(3) + + const db = testCommon.factory() + await db.open() + + t.ok(db.supports.events.put) + + db.on('put', function (key, value) { + t.is(key, 123) + t.is(value, 'b') + }) + + await db.put(123, 'b') + await db.close() + }) +} + +exports.tearDown = function (test, testCommon) { + test('tearDown', function (t) { + db.close(t.end.bind(t)) + }) +} + +exports.all = function (test, testCommon) { + exports.setUp(test, testCommon) + exports.args(test, testCommon) + exports.put(test, testCommon) + exports.events(test, testCommon) + exports.tearDown(test, testCommon) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self.js new file mode 100644 index 0000000..d4e9761 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self.js @@ -0,0 +1,1080 @@ +'use strict' + +const test = require('tape') +const sinon = require('sinon') +const isBuffer = require('is-buffer') +const { Buffer } = require('buffer') +const { AbstractLevel, AbstractChainedBatch } = require('..') +const { MinimalLevel } = require('./util') +const getRangeOptions = require('../lib/range-options') + +const testCommon = require('./common')({ + test, + factory () { + return new AbstractLevel({ encodings: { utf8: true } }) + } +}) + +const rangeOptions = ['gt', 'gte', 'lt', 'lte'] + +function implement (ctor, methods) { + class Test extends ctor {} + + for (const k in methods) { + Test.prototype[k] = methods[k] + } + + return Test +} + +// Temporary test for browsers +// Not supported on Safari < 12 and Safari iOS < 12 +test('async generator', async function (t) { + let ended = false + + const end = async () => { + await new Promise((resolve) => setTimeout(resolve, 100)) + ended = true + } + + async function * generator () { + try { + yield 1 + yield 2 + yield 3 + yield 4 + } finally { + // Test that we're always able to cleanup resources + await end() + } + } + + const res = [] + + for await (const x of generator()) { + res.push(x) + if (x === 2) break + } + + t.same(res, [1, 2]) + t.is(ended, true) + + ended = false + + try { + for await (const x of generator()) { + res.push(x) + if (x === 2) throw new Error('userland error') + } + } catch (err) { + t.is(err.message, 'userland error') + } + + t.same(res, [1, 2, 1, 2]) + t.is(ended, true) +}) + +/** + * Extensibility + */ + +test('test core extensibility', function (t) { + const Test = implement(AbstractLevel) + const test = new Test({ encodings: { utf8: true } }) + t.equal(test.status, 'opening', 'status is opening') + t.end() +}) + +test('test open() extensibility when new', function (t) { + const spy = sinon.spy() + const expectedCb = function () {} + const expectedOptions = { createIfMissing: true, errorIfExists: false } + const Test = implement(AbstractLevel, { _open: spy }) + const test = new Test({ encodings: { utf8: true } }) + const test2 = new Test({ encodings: { utf8: true } }) + + test.open(expectedCb) + + t.equal(spy.callCount, 1, 'got _open() call') + t.equal(spy.getCall(0).thisValue, test, '`this` on _open() was correct') + t.equal(spy.getCall(0).args.length, 2, 'got two arguments') + t.deepEqual(spy.getCall(0).args[0], expectedOptions, 'got default options argument') + + test2.open({ options: 1 }, expectedCb) + + expectedOptions.options = 1 + + t.equal(spy.callCount, 2, 'got _open() call') + t.equal(spy.getCall(1).thisValue, test2, '`this` on _open() was correct') + t.equal(spy.getCall(1).args.length, 2, 'got two arguments') + t.deepEqual(spy.getCall(1).args[0], expectedOptions, 'got expected options argument') + t.end() +}) + +test('test open() extensibility when open', function (t) { + t.plan(3) + + const spy = sinon.spy(function (options, cb) { this.nextTick(cb) }) + const Test = implement(AbstractLevel, { _open: spy }) + const test = new Test({ encodings: { utf8: true } }) + + test.once('open', function () { + t.is(spy.callCount, 1, 'got _open() call') + + test.open(t.ifError.bind(t)) + + t.is(spy.callCount, 1, 'did not get second _open() call') + }) +}) + +test('test opening explicitly gives a chance to capture an error', function (t) { + t.plan(3) + + const spy = sinon.spy(function (options, cb) { this.nextTick(cb, new Error('_open error')) }) + const Test = implement(AbstractLevel, { _open: spy }) + const test = new Test({ encodings: { utf8: true } }) + + test.open(function (err) { + t.is(spy.callCount, 1, 'got _open() call') + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + t.is(err && err.cause && err.cause.message, '_open error') + }) +}) + +test('test opening explicitly gives a chance to capture an error with promise', async function (t) { + t.plan(3) + + const spy = sinon.spy(function (options, cb) { this.nextTick(cb, new Error('_open error')) }) + const Test = implement(AbstractLevel, { _open: spy }) + const test = new Test({ encodings: { utf8: true } }) + + try { + await test.open() + } catch (err) { + t.is(spy.callCount, 1, 'got _open() call') + t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN') + t.is(err.cause && err.cause.message, '_open error') + } +}) + +test('test constructor options are forwarded to open()', function (t) { + t.plan(3) + + const spy = sinon.spy(function (options, cb) { this.nextTick(cb) }) + const Test = implement(AbstractLevel, { _open: spy }) + const test = new Test({ encodings: { utf8: true } }, { + passive: true, + keyEncoding: 'json', + valueEncoding: 'json', + createIfMissing: false, + foo: 123 + }) + + test.open(function (err) { + t.ifError(err, 'no open() error') + t.is(spy.callCount, 1, 'got _open() call') + t.same(spy.getCall(0).args[0], { + foo: 123, + createIfMissing: false, + errorIfExists: false + }, 'does not forward passive, keyEncoding and valueEncoding options') + }) +}) + +test('test close() extensibility when open', function (t) { + t.plan(4) + + const spy = sinon.spy(function (cb) { this.nextTick(cb) }) + const Test = implement(AbstractLevel, { _close: spy }) + const test = new Test({ encodings: { utf8: true } }) + + test.once('open', function () { + test.close(function (err) { + t.ifError(err, 'no close() error') + t.is(spy.callCount, 1, 'got _close() call') + t.is(spy.getCall(0).thisValue, test, '`this` on _close() was correct') + t.is(spy.getCall(0).args.length, 1, 'got one arguments') + }) + }) +}) + +test('test close() extensibility when new', function (t) { + t.plan(3) + + const spy = sinon.spy(function (cb) { this.nextTick(cb) }) + const Test = implement(AbstractLevel, { _close: spy }) + const test = new Test({ encodings: { utf8: true } }) + + test.close(function (err) { + t.ifError(err, 'no close() error') + t.is(spy.callCount, 1, 'did get _close() call') + }) + + t.is(spy.callCount, 0, 'did not get _close() call') +}) + +test('test open(), close(), open() with twice failed open', function (t) { + t.plan(8) + + const db = testCommon.factory() + const order = [] + + let opens = 0 + + db.on('open', t.fail.bind(t)) + db.on('closed', t.fail.bind(t)) + + db._open = function (options, callback) { + t.pass('called') + this.nextTick(callback, new Error('test' + (++opens))) + } + + db.open(function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + t.is(err && err.cause && err.cause.message, 'test1') + order.push('A') + }) + + db.close(function (err) { + t.ifError(err) + order.push('B') + }) + + db.open(function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + t.is(err && err.cause && err.cause.message, 'test2') + order.push('C') + t.same(order, ['A', 'B', 'C'], 'order is ok') + }) +}) + +test('test open(), close(), open() with first failed open', function (t) { + t.plan(9) + + const db = testCommon.factory() + const order = [] + + let opens = 0 + + db.on('open', () => { order.push('open event') }) + db.on('closed', t.fail.bind(t)) + + db._open = function (options, callback) { + t.pass('called') + this.nextTick(callback, opens++ ? null : new Error('test')) + } + + db.open(function (err) { + t.ifError(err) + t.is(db.status, 'open') + order.push('A') + }) + + db.close(function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_CLOSED') + t.is(db.status, 'open') + order.push('B') + }) + + db.open(function (err) { + t.ifError(err) + t.is(db.status, 'open') + order.push('C') + t.same(order, ['A', 'B', 'open event', 'C'], 'order is ok') + }) +}) + +test('test open(), close(), open() with second failed open', function (t) { + t.plan(10) + + const db = testCommon.factory() + const order = [] + + let opens = 0 + + db.on('open', t.fail.bind(t)) + db.on('closed', t.fail.bind(t)) + + db._open = function (options, callback) { + t.pass('called') + this.nextTick(callback, opens++ ? new Error('test') : null) + } + + db.open(function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + t.is(db.status, 'closed') + order.push('A') + }) + + db.close(function (err) { + t.ifError(err) + t.is(db.status, 'closed') + order.push('B') + }) + + db.open(function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + t.is(err && err.cause.message, 'test') + t.is(db.status, 'closed') + order.push('C') + t.same(order, ['A', 'B', 'C'], 'order is ok') + }) +}) + +test('test get() extensibility', function (t) { + t.plan(12) + + const spy = sinon.spy() + const expectedCb = function () {} + const expectedOptions = { keyEncoding: 'utf8', valueEncoding: 'utf8' } + const expectedKey = 'a key' + const Test = implement(AbstractLevel, { _get: spy }) + const test = new Test({ encodings: { utf8: true } }, { keyEncoding: 'utf8' }) + + test.once('open', function () { + test.get(expectedKey, expectedCb) + + t.equal(spy.callCount, 1, 'got _get() call') + t.equal(spy.getCall(0).thisValue, test, '`this` on _get() was correct') + t.equal(spy.getCall(0).args.length, 3, 'got three arguments') + t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument') + t.deepEqual(spy.getCall(0).args[1], expectedOptions, 'got default options argument') + t.is(typeof spy.getCall(0).args[2], 'function', 'got cb argument') + + test.get(expectedKey, { options: 1 }, expectedCb) + + expectedOptions.options = 1 + + t.equal(spy.callCount, 2, 'got _get() call') + t.equal(spy.getCall(1).thisValue, test, '`this` on _get() was correct') + t.equal(spy.getCall(1).args.length, 3, 'got three arguments') + t.equal(spy.getCall(1).args[0], expectedKey, 'got expected key argument') + t.deepEqual(spy.getCall(1).args[1], expectedOptions, 'got expected options argument') + t.is(typeof spy.getCall(1).args[2], 'function', 'got cb argument') + }) +}) + +test('test getMany() extensibility', function (t) { + t.plan(12) + + const spy = sinon.spy() + const expectedCb = function () {} + const expectedOptions = { keyEncoding: 'utf8', valueEncoding: 'utf8' } + const expectedKey = 'a key' + const Test = implement(AbstractLevel, { _getMany: spy }) + const test = new Test({ encodings: { utf8: true } }) + + test.once('open', function () { + test.getMany([expectedKey], expectedCb) + + t.equal(spy.callCount, 1, 'got _getMany() call') + t.equal(spy.getCall(0).thisValue, test, '`this` on _getMany() was correct') + t.equal(spy.getCall(0).args.length, 3, 'got three arguments') + t.deepEqual(spy.getCall(0).args[0], [expectedKey], 'got expected keys argument') + t.deepEqual(spy.getCall(0).args[1], expectedOptions, 'got default options argument') + t.is(typeof spy.getCall(0).args[2], 'function', 'got cb argument') + + test.getMany([expectedKey], { options: 1 }, expectedCb) + + expectedOptions.options = 1 + + t.equal(spy.callCount, 2, 'got _getMany() call') + t.equal(spy.getCall(1).thisValue, test, '`this` on _getMany() was correct') + t.equal(spy.getCall(1).args.length, 3, 'got three arguments') + t.deepEqual(spy.getCall(1).args[0], [expectedKey], 'got expected key argument') + t.deepEqual(spy.getCall(1).args[1], expectedOptions, 'got expected options argument') + t.is(typeof spy.getCall(1).args[2], 'function', 'got cb argument') + }) +}) + +test('test del() extensibility', function (t) { + t.plan(12) + + const spy = sinon.spy() + const expectedCb = function () {} + const expectedOptions = { options: 1, keyEncoding: 'utf8' } + const expectedKey = 'a key' + const Test = implement(AbstractLevel, { _del: spy }) + const test = new Test({ encodings: { utf8: true } }) + + test.once('open', function () { + test.del(expectedKey, expectedCb) + + t.equal(spy.callCount, 1, 'got _del() call') + t.equal(spy.getCall(0).thisValue, test, '`this` on _del() was correct') + t.equal(spy.getCall(0).args.length, 3, 'got three arguments') + t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument') + t.deepEqual(spy.getCall(0).args[1], { keyEncoding: 'utf8' }, 'got blank options argument') + t.equal(typeof spy.getCall(0).args[2], 'function', 'got cb argument') + + test.del(expectedKey, expectedOptions, expectedCb) + + t.equal(spy.callCount, 2, 'got _del() call') + t.equal(spy.getCall(1).thisValue, test, '`this` on _del() was correct') + t.equal(spy.getCall(1).args.length, 3, 'got three arguments') + t.equal(spy.getCall(1).args[0], expectedKey, 'got expected key argument') + t.deepEqual(spy.getCall(1).args[1], expectedOptions, 'got expected options argument') + t.equal(typeof spy.getCall(1).args[2], 'function', 'got cb argument') + }) +}) + +test('test put() extensibility', function (t) { + t.plan(14) + + const spy = sinon.spy() + const expectedCb = function () {} + const expectedOptions = { options: 1, keyEncoding: 'utf8', valueEncoding: 'utf8' } + const expectedKey = 'a key' + const expectedValue = 'a value' + const Test = implement(AbstractLevel, { _put: spy }) + const test = new Test({ encodings: { utf8: true } }) + + test.once('open', function () { + test.put(expectedKey, expectedValue, expectedCb) + + t.equal(spy.callCount, 1, 'got _put() call') + t.equal(spy.getCall(0).thisValue, test, '`this` on _put() was correct') + t.equal(spy.getCall(0).args.length, 4, 'got four arguments') + t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument') + t.equal(spy.getCall(0).args[1], expectedValue, 'got expected value argument') + t.deepEqual(spy.getCall(0).args[2], { keyEncoding: 'utf8', valueEncoding: 'utf8' }, 'got default options argument') + t.equal(typeof spy.getCall(0).args[3], 'function', 'got cb argument') + + test.put(expectedKey, expectedValue, expectedOptions, expectedCb) + + t.equal(spy.callCount, 2, 'got _put() call') + t.equal(spy.getCall(1).thisValue, test, '`this` on _put() was correct') + t.equal(spy.getCall(1).args.length, 4, 'got four arguments') + t.equal(spy.getCall(1).args[0], expectedKey, 'got expected key argument') + t.equal(spy.getCall(1).args[1], expectedValue, 'got expected value argument') + t.deepEqual(spy.getCall(1).args[2], expectedOptions, 'got expected options argument') + t.equal(typeof spy.getCall(1).args[3], 'function', 'got cb argument') + }) +}) + +test('test batch([]) (array-form) extensibility', function (t) { + t.plan(18) + + const spy = sinon.spy() + const expectedCb = function () {} + const expectedOptions = { options: 1 } + const expectedArray = [ + { type: 'put', key: '1', value: '1', keyEncoding: 'utf8', valueEncoding: 'utf8' }, + { type: 'del', key: '2', keyEncoding: 'utf8' } + ] + const Test = implement(AbstractLevel, { _batch: spy }) + const test = new Test({ encodings: { utf8: true } }) + + test.once('open', function () { + test.batch(expectedArray, expectedCb) + + t.equal(spy.callCount, 1, 'got _batch() call') + t.equal(spy.getCall(0).thisValue, test, '`this` on _batch() was correct') + t.equal(spy.getCall(0).args.length, 3, 'got three arguments') + t.deepEqual(spy.getCall(0).args[0], expectedArray, 'got expected array argument') + t.deepEqual(spy.getCall(0).args[1], {}, 'got expected options argument') + t.equal(typeof spy.getCall(0).args[2], 'function', 'got callback argument') + + test.batch(expectedArray, expectedOptions, expectedCb) + + t.equal(spy.callCount, 2, 'got _batch() call') + t.equal(spy.getCall(1).thisValue, test, '`this` on _batch() was correct') + t.equal(spy.getCall(1).args.length, 3, 'got three arguments') + t.deepEqual(spy.getCall(1).args[0], expectedArray, 'got expected array argument') + t.deepEqual(spy.getCall(1).args[1], expectedOptions, 'got expected options argument') + t.equal(typeof spy.getCall(1).args[2], 'function', 'got callback argument') + + test.batch(expectedArray, null, expectedCb) + + t.equal(spy.callCount, 3, 'got _batch() call') + t.equal(spy.getCall(2).thisValue, test, '`this` on _batch() was correct') + t.equal(spy.getCall(2).args.length, 3, 'got three arguments') + t.deepEqual(spy.getCall(2).args[0], expectedArray, 'got expected array argument') + t.ok(spy.getCall(2).args[1], 'options should not be null') + t.equal(typeof spy.getCall(2).args[2], 'function', 'got callback argument') + }) +}) + +test('test batch([]) (array-form) with empty array is asynchronous', function (t) { + t.plan(3) + + const spy = sinon.spy() + const Test = implement(AbstractLevel, { _batch: spy }) + const test = new Test({ encodings: { utf8: true } }) + + test.once('open', function () { + let async = false + + test.batch([], function (err) { + t.ifError(err, 'no error') + t.ok(async, 'callback is asynchronous') + + // Assert that asynchronicity is provided by batch() rather than _batch() + t.is(spy.callCount, 0, '_batch() call was bypassed') + }) + + async = true + }) +}) + +test('test chained batch() extensibility', function (t) { + t.plan(16) + + const spy = sinon.spy() + const expectedCb = function () {} + const expectedOptions = { options: 1 } + const Test = implement(AbstractLevel, { _batch: spy }) + const test = new Test({ encodings: { utf8: true } }) + + test.once('open', function () { + test.batch().put('foo', 'bar').del('bang').write(expectedCb) + + t.equal(spy.callCount, 1, 'got _batch() call') + t.equal(spy.getCall(0).thisValue, test, '`this` on _batch() was correct') + t.equal(spy.getCall(0).args.length, 3, 'got three arguments') + t.equal(spy.getCall(0).args[0].length, 2, 'got expected array argument') + t.deepEqual(spy.getCall(0).args[0][0], { keyEncoding: 'utf8', valueEncoding: 'utf8', type: 'put', key: 'foo', value: 'bar' }, 'got expected array argument[0]') + t.deepEqual(spy.getCall(0).args[0][1], { keyEncoding: 'utf8', type: 'del', key: 'bang' }, 'got expected array argument[1]') + t.deepEqual(spy.getCall(0).args[1], {}, 'got expected options argument') + t.is(typeof spy.getCall(0).args[2], 'function', 'got callback argument') + + test.batch().put('foo', 'bar', expectedOptions).del('bang', expectedOptions).write(expectedOptions, expectedCb) + + t.equal(spy.callCount, 2, 'got _batch() call') + t.equal(spy.getCall(1).thisValue, test, '`this` on _batch() was correct') + t.equal(spy.getCall(1).args.length, 3, 'got three arguments') + t.equal(spy.getCall(1).args[0].length, 2, 'got expected array argument') + t.deepEqual(spy.getCall(1).args[0][0], { keyEncoding: 'utf8', valueEncoding: 'utf8', type: 'put', key: 'foo', value: 'bar', options: 1 }, 'got expected array argument[0]') + t.deepEqual(spy.getCall(1).args[0][1], { keyEncoding: 'utf8', type: 'del', key: 'bang', options: 1 }, 'got expected array argument[1]') + t.deepEqual(spy.getCall(1).args[1], { options: 1 }, 'got expected options argument') + t.is(typeof spy.getCall(1).args[2], 'function', 'got callback argument') + }) +}) + +test('test chained batch() with no operations is asynchronous', function (t) { + t.plan(2) + + const Test = implement(AbstractLevel, {}) + const test = new Test({ encodings: { utf8: true } }) + + test.once('open', function () { + let async = false + + test.batch().write(function (err) { + t.ifError(err, 'no error') + t.ok(async, 'callback is asynchronous') + }) + + async = true + }) +}) + +test('test chained batch() (custom _chainedBatch) extensibility', function (t) { + t.plan(4) + + const spy = sinon.spy() + const Test = implement(AbstractLevel, { _chainedBatch: spy }) + const test = new Test({ encodings: { utf8: true } }) + + test.once('open', function () { + test.batch() + + t.equal(spy.callCount, 1, 'got _chainedBatch() call') + t.equal(spy.getCall(0).thisValue, test, '`this` on _chainedBatch() was correct') + + test.batch() + + t.equal(spy.callCount, 2, 'got _chainedBatch() call') + t.equal(spy.getCall(1).thisValue, test, '`this` on _chainedBatch() was correct') + }) +}) + +test('test AbstractChainedBatch extensibility', function (t) { + const Batch = implement(AbstractChainedBatch) + const db = testCommon.factory() + const test = new Batch(db) + t.ok(test.db === db, 'instance has db reference') + t.end() +}) + +test('test AbstractChainedBatch expects a db', function (t) { + t.plan(1) + + const Test = implement(AbstractChainedBatch) + + try { + // eslint-disable-next-line no-new + new Test() + } catch (err) { + t.is(err.message, 'The first argument must be an abstract-level database, received undefined') + } +}) + +test('test AbstractChainedBatch#write() extensibility', function (t) { + t.plan(3) + + let batch + + const Test = implement(AbstractChainedBatch, { + _write: function (options, callback) { + t.same(options, {}) + t.is(this, batch, 'thisArg on _write() is correct') + this.nextTick(callback) + } + }) + + const db = testCommon.factory() + + db.once('open', function () { + batch = new Test(db) + + // Without any operations, _write isn't called + batch.put('foo', 'bar') + batch.write(function (err) { + t.ifError(err) + }) + }) +}) + +test('test AbstractChainedBatch#write() extensibility with null options', function (t) { + t.plan(3) + + let batch + + const Test = implement(AbstractChainedBatch, { + _write: function (options, callback) { + t.same(options, {}) + t.is(this, batch, 'thisArg on _write() is correct') + this.nextTick(callback) + } + }) + + const db = testCommon.factory() + + db.once('open', function () { + batch = new Test(db) + + // Without any operations, _write isn't called + batch.put('foo', 'bar') + batch.write(null, function (err) { + t.ifError(err) + }) + }) +}) + +test('test AbstractChainedBatch#write() extensibility with options', function (t) { + t.plan(3) + + let batch + + const Test = implement(AbstractChainedBatch, { + _write: function (options, callback) { + t.same(options, { test: true }) + t.is(this, batch, 'thisArg on _write() is correct') + this.nextTick(callback) + } + }) + + const db = testCommon.factory() + + db.once('open', function () { + batch = new Test(db) + // Without any operations, _write isn't called + batch.put('foo', 'bar') + batch.write({ test: true }, function (err) { + t.ifError(err) + }) + }) +}) + +test('test AbstractChainedBatch#put() extensibility', function (t) { + t.plan(7) + + const spy = sinon.spy() + const expectedKey = 'key' + const expectedValue = 'value' + const Test = implement(AbstractChainedBatch, { _put: spy }) + const db = testCommon.factory() + + db.once('open', function () { + const test = new Test(db) + const returnValue = test.put(expectedKey, expectedValue) + + t.equal(spy.callCount, 1, 'got _put call') + t.equal(spy.getCall(0).thisValue, test, '`this` on _put() was correct') + t.equal(spy.getCall(0).args.length, 3, 'got 3 arguments') + t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument') + t.equal(spy.getCall(0).args[1], expectedValue, 'got expected value argument') + t.same(spy.getCall(0).args[2], { keyEncoding: 'utf8', valueEncoding: 'utf8' }, 'got expected options argument') + t.equal(returnValue, test, 'get expected return value') + }) +}) + +test('test AbstractChainedBatch#del() extensibility', function (t) { + t.plan(6) + + const spy = sinon.spy() + const expectedKey = 'key' + const Test = implement(AbstractChainedBatch, { _del: spy }) + const db = testCommon.factory() + + db.once('open', function () { + const test = new Test(db) + const returnValue = test.del(expectedKey) + + t.equal(spy.callCount, 1, 'got _del call') + t.equal(spy.getCall(0).thisValue, test, '`this` on _del() was correct') + t.equal(spy.getCall(0).args.length, 2, 'got 2 arguments') + t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument') + t.same(spy.getCall(0).args[1], { keyEncoding: 'utf8' }, 'got expected options argument') + t.equal(returnValue, test, 'get expected return value') + }) +}) + +test('test AbstractChainedBatch#clear() extensibility', function (t) { + t.plan(4) + + const spy = sinon.spy() + const Test = implement(AbstractChainedBatch, { _clear: spy }) + const db = testCommon.factory() + + db.once('open', function () { + const test = new Test(db) + const returnValue = test.clear() + + t.equal(spy.callCount, 1, 'got _clear call') + t.equal(spy.getCall(0).thisValue, test, '`this` on _clear() was correct') + t.equal(spy.getCall(0).args.length, 0, 'got zero arguments') + t.equal(returnValue, test, 'get expected return value') + }) +}) + +test('test clear() extensibility', function (t) { + t.plan((7 * 5) - 4) + + const spy = sinon.spy() + const Test = implement(AbstractLevel, { _clear: spy }) + const db = new Test({ encodings: { utf8: true } }) + const callback = function () {} + + db.once('open', function () { + call([callback], { keyEncoding: 'utf8', reverse: false, limit: -1 }) + call([null, callback], { keyEncoding: 'utf8', reverse: false, limit: -1 }) + call([undefined, callback], { keyEncoding: 'utf8', reverse: false, limit: -1 }) + call([{ custom: 1 }, callback], { custom: 1, keyEncoding: 'utf8', reverse: false, limit: -1 }) + call([{ reverse: true, limit: 0 }, callback], { keyEncoding: 'utf8', reverse: true, limit: 0 }, true) + call([{ reverse: 1 }, callback], { keyEncoding: 'utf8', reverse: true, limit: -1 }) + call([{ reverse: null }, callback], { keyEncoding: 'utf8', reverse: false, limit: -1 }) + + function call (args, expectedOptions, shouldSkipCall) { + db.clear.apply(db, args) + + t.is(spy.callCount, shouldSkipCall ? 0 : 1, 'got _clear() call') + + if (!shouldSkipCall) { + t.is(spy.getCall(0).thisValue, db, '`this` on _clear() was correct') + t.is(spy.getCall(0).args.length, 2, 'got two arguments') + t.same(spy.getCall(0).args[0], expectedOptions, 'got expected options argument') + t.is(typeof spy.getCall(0).args[1], 'function', 'got callback argument') + } + + spy.resetHistory() + } + }) +}) + +// TODO: replace with encoding test +test.skip('test serialization extensibility (batch array is not mutated)', function (t) { + t.plan(7) + + const spy = sinon.spy() + const Test = implement(AbstractLevel, { + _batch: spy, + _serializeKey: function (key) { + t.equal(key, 'no') + return 'foo' + }, + _serializeValue: function (value) { + t.equal(value, 'nope') + return 'bar' + } + }) + + const test = new Test({ encodings: { utf8: true } }) + + test.once('open', function () { + const op = { type: 'put', key: 'no', value: 'nope' } + + test.batch([op], function () {}) + + t.equal(spy.callCount, 1, 'got _batch() call') + t.equal(spy.getCall(0).args[0][0].key, 'foo', 'got expected key') + t.equal(spy.getCall(0).args[0][0].value, 'bar', 'got expected value') + + t.equal(op.key, 'no', 'did not mutate input key') + t.equal(op.value, 'nope', 'did not mutate input value') + }) +}) + +test('clear() does not delete empty or nullish range options', function (t) { + const rangeValues = [Uint8Array.from([]), '', null, undefined] + + t.plan(rangeOptions.length * rangeValues.length) + + rangeValues.forEach(function (value) { + const Test = implement(AbstractLevel, { + _clear: function (options, callback) { + rangeOptions.forEach(function (key) { + t.ok(key in options, key + ' option should not be deleted') + }) + } + }) + + const db = new Test({ encodings: { utf8: true } }) + const options = {} + + rangeOptions.forEach(function (key) { + options[key] = value + }) + + db.once('open', function () { + db.clear(options, function () {}) + }) + }) +}) + +// TODO: some of these are redundant +test('.status', function (t) { + t.plan(5) + + t.test('empty prototype', function (t) { + const Test = implement(AbstractLevel) + const test = new Test({ encodings: { utf8: true } }) + + t.equal(test.status, 'opening') + + test.open(function (err) { + t.error(err) + t.equal(test.status, 'open') + + test.close(function (err) { + t.error(err) + t.equal(test.status, 'closed') + t.end() + }) + }) + + t.equal(test.status, 'opening') + }) + + t.test('open error', function (t) { + const Test = implement(AbstractLevel, { + _open: function (options, cb) { + cb(new Error('_open error')) + } + }) + + const test = new Test({ encodings: { utf8: true } }) + + test.open(function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + t.is(err && err.cause && err.cause.message, '_open error') + t.equal(test.status, 'closed') + t.end() + }) + }) + + t.test('close error', function (t) { + const Test = implement(AbstractLevel, { + _close: function (cb) { + cb(new Error('_close error')) + } + }) + + const test = new Test({ encodings: { utf8: true } }) + test.open(function () { + test.close(function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_CLOSED') + t.is(err && err.cause && err.cause.message, '_close error') + t.equal(test.status, 'open') + t.end() + }) + }) + }) + + t.test('open', function (t) { + const Test = implement(AbstractLevel, { + _open: function (options, cb) { + this.nextTick(cb) + } + }) + + const test = new Test({ encodings: { utf8: true } }) + test.open(function (err) { + t.error(err) + t.equal(test.status, 'open') + t.end() + }) + t.equal(test.status, 'opening') + }) + + t.test('close', function (t) { + const Test = implement(AbstractLevel, { + _close: function (cb) { + this.nextTick(cb) + } + }) + + const test = new Test({ encodings: { utf8: true } }) + test.open(function (err) { + t.error(err) + test.close(function (err) { + t.error(err) + t.equal(test.status, 'closed') + t.end() + }) + t.equal(test.status, 'closing') + }) + }) +}) + +test('rangeOptions', function (t) { + const keys = rangeOptions.slice() + const db = new AbstractLevel({ + encodings: { + utf8: true, buffer: true, view: true + } + }) + + function setupOptions (create) { + const options = {} + for (const key of keys) { + options[key] = create() + } + return options + } + + function verifyOptions (t, options) { + for (const key of keys) { + t.ok(key in options, key + ' option should not be deleted') + } + t.end() + } + + t.plan(11) + t.test('setup', async (t) => db.open()) + + t.test('default options', function (t) { + t.same(getRangeOptions(undefined, db.keyEncoding('utf8')), { + reverse: false, + limit: -1 + }, 'correct defaults') + t.end() + }) + + t.test('set options', function (t) { + t.same(getRangeOptions({ reverse: false, limit: 20 }, db.keyEncoding('utf8')), { + reverse: false, + limit: 20 + }, 'options set correctly') + t.end() + }) + + t.test('ignores invalid limit', function (t) { + // Infinity is valid but is normalized to -1 for use in private API + for (const limit of [Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, NaN, -2, 5.5]) { + t.same(getRangeOptions({ limit }, db.keyEncoding('utf8')).limit, -1) + } + t.end() + }) + + t.test('ignores not-own property', function (t) { + class Options {} + Options.prototype.limit = 20 + const options = new Options() + + t.is(options.limit, 20) + t.same(getRangeOptions(options, db.keyEncoding('utf8')), { + reverse: false, + limit: -1 + }) + t.end() + }) + + t.test('does not delete empty buffers', function (t) { + const options = setupOptions(() => Buffer.alloc(0)) + keys.forEach(function (key) { + t.is(isBuffer(options[key]), true, 'should be buffer') + t.is(options[key].byteLength, 0, 'should be empty') + }) + verifyOptions(t, getRangeOptions(options, db.keyEncoding('buffer'))) + }) + + t.test('does not delete empty views', function (t) { + const options = setupOptions(() => Uint8Array.from([])) + keys.forEach(function (key) { + t.is(options[key] instanceof Uint8Array, true, 'should be Uint8Array') + t.is(options[key].byteLength, 0, 'should be empty') + }) + verifyOptions(t, getRangeOptions(options, db.keyEncoding('view'))) + }) + + t.test('does not delete empty strings', function (t) { + const options = setupOptions(() => '') + keys.forEach(function (key) { + t.is(typeof options[key], 'string', 'should be string') + t.is(options[key].length, 0, 'should be empty') + }) + verifyOptions(t, getRangeOptions(options, db.keyEncoding('utf8'))) + }) + + t.test('does not delete null', function (t) { + const options = setupOptions(() => null) + keys.forEach(function (key) { + t.is(options[key], null) + }) + verifyOptions(t, getRangeOptions(options, db.keyEncoding('utf8'))) + }) + + t.test('does not delete undefined', function (t) { + const options = setupOptions(() => undefined) + keys.forEach(function (key) { + t.is(options[key], undefined) + }) + verifyOptions(t, getRangeOptions(options, db.keyEncoding('utf8'))) + }) + + t.test('rejects legacy range options', function (t) { + t.plan(2) + + for (const key of ['start', 'end']) { + const options = {} + options[key] = 'x' + + try { + getRangeOptions(options, db.keyEncoding('utf8')) + } catch (err) { + t.is(err.code, 'LEVEL_LEGACY') + } + } + }) +}) + +require('./self/defer-test') +require('./self/attach-resource-test') +require('./self/abstract-iterator-test') +require('./self/iterator-test') +require('./self/deferred-iterator-test') +require('./self/deferred-operations-test') +require('./self/deferred-chained-batch-test') +require('./self/async-iterator-test') +require('./self/encoding-test') +require('./self/sublevel-test') + +// Test the abstract test suite using a minimal implementation +require('./index')({ + test, + factory (options) { + return new MinimalLevel(options) + } +}) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/abstract-iterator-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/abstract-iterator-test.js new file mode 100644 index 0000000..6f23aff --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/abstract-iterator-test.js @@ -0,0 +1,263 @@ +'use strict' + +const test = require('tape') +const { AbstractLevel, AbstractIterator, AbstractKeyIterator, AbstractValueIterator } = require('../..') + +const testCommon = require('../common')({ + test, + factory: function () { + return new AbstractLevel({ encodings: { utf8: true } }) + } +}) + +for (const Ctor of [AbstractIterator, AbstractKeyIterator, AbstractValueIterator]) { + test(`test ${Ctor.name} extensibility`, function (t) { + const Test = class TestIterator extends Ctor {} + const db = testCommon.factory() + const test = new Test(db, {}) + t.ok(test.db === db, 'instance has db reference') + t.end() + }) + + test(`${Ctor.name} throws on invalid db argument`, function (t) { + t.plan(4 * 2) + + for (const args of [[], [null], [undefined], 'foo']) { + const hint = args[0] === null ? 'null' : typeof args[0] + + try { + // eslint-disable-next-line no-new + new Ctor(...args) + } catch (err) { + t.is(err.name, 'TypeError') + t.is(err.message, 'The first argument must be an abstract-level database, received ' + hint) + } + } + }) + + test(`${Ctor.name} throws on invalid options argument`, function (t) { + t.plan(4 * 2) + + for (const args of [[], [null], [undefined], 'foo']) { + try { + // eslint-disable-next-line no-new + new Ctor({}, ...args) + } catch (err) { + t.is(err.name, 'TypeError') + t.is(err.message, 'The second argument must be an options object') + } + } + }) + + test(`${Ctor.name}.next() extensibility`, async function (t) { + t.plan(3) + + class TestIterator extends Ctor { + _next (callback) { + t.is(this, it, 'thisArg on _next() was correct') + t.is(arguments.length, 1, 'got one argument') + t.is(typeof callback, 'function', 'got a callback function') + this.nextTick(callback) + } + } + + const db = testCommon.factory() + await db.open() + const it = new TestIterator(db, {}) + await it.next() + await db.close() + }) + + test(`${Ctor.name}.next() throws on invalid callback argument`, async function (t) { + t.plan(3 * 2) + + const db = testCommon.factory() + await db.open() + + for (const invalid of [{}, null, 'foo']) { + const it = new Ctor(db, {}) + + try { + it.next(invalid) + } catch (err) { + t.is(err.name, 'TypeError') + t.is(err.message, 'Callback must be a function') + } + } + }) + + test(`${Ctor.name}.nextv() extensibility`, async function (t) { + t.plan(5 * 2) + + class TestIterator extends Ctor { + _nextv (size, options, callback) { + t.is(this, it, 'thisArg on _nextv() was correct') + t.is(arguments.length, 3, 'got 3 arguments') + t.is(size, 100) + t.same(options, {}, 'empty options') + t.is(typeof callback, 'function', 'got a callback function') + this.nextTick(callback, null, []) + } + } + + const db = testCommon.factory() + await db.open() + const it = new TestIterator(db, {}) + await it.nextv(100) + await it.nextv(100, {}) + await db.close() + }) + + test(`${Ctor.name}.nextv() extensibility (options)`, async function (t) { + t.plan(2) + + class TestIterator extends Ctor { + _nextv (size, options, callback) { + t.is(size, 100) + t.same(options, { foo: 123 }, 'got options') + this.nextTick(callback, null, []) + } + } + + const db = testCommon.factory() + await db.open() + const it = new TestIterator(db, {}) + await it.nextv(100, { foo: 123 }) + await db.close() + }) + + test(`${Ctor.name}.nextv() throws on invalid callback argument`, async function (t) { + t.plan(3 * 2) + + const db = testCommon.factory() + await db.open() + + for (const invalid of [{}, null, 'foo']) { + const it = new Ctor(db, {}) + + try { + it.nextv(100, {}, invalid) + } catch (err) { + t.is(err.name, 'TypeError') + t.is(err.message, 'Callback must be a function') + } + } + }) + + test(`${Ctor.name}.all() extensibility`, async function (t) { + t.plan(2 * 4) + + for (const args of [[], [{}]]) { + class TestIterator extends Ctor { + _all (options, callback) { + t.is(this, it, 'thisArg on _all() was correct') + t.is(arguments.length, 2, 'got 2 arguments') + t.same(options, {}, 'empty options') + t.is(typeof callback, 'function', 'got a callback function') + this.nextTick(callback, null, []) + } + } + + const db = testCommon.factory() + await db.open() + const it = new TestIterator(db, {}) + await it.all(...args) + await db.close() + } + }) + + test(`${Ctor.name}.all() extensibility (options)`, async function (t) { + t.plan(1) + + class TestIterator extends Ctor { + _all (options, callback) { + t.same(options, { foo: 123 }, 'got options') + this.nextTick(callback, null, []) + } + } + + const db = testCommon.factory() + await db.open() + const it = new TestIterator(db, {}) + await it.all({ foo: 123 }) + await db.close() + }) + + test(`${Ctor.name}.all() throws on invalid callback argument`, async function (t) { + t.plan(3 * 2) + + const db = testCommon.factory() + await db.open() + + for (const invalid of [{}, null, 'foo']) { + const it = new Ctor(db, {}) + + try { + it.all({}, invalid) + } catch (err) { + t.is(err.name, 'TypeError') + t.is(err.message, 'Callback must be a function') + } + } + }) + + test(`${Ctor.name}.close() extensibility`, async function (t) { + t.plan(3) + + class TestIterator extends Ctor { + _close (callback) { + t.is(this, it, 'thisArg on _close() was correct') + t.is(arguments.length, 1, 'got one argument') + t.is(typeof callback, 'function', 'got a callback function') + this.nextTick(callback) + } + } + + const db = testCommon.factory() + await db.open() + const it = new TestIterator(db, {}) + await it.close() + await db.close() + }) + + test(`${Ctor.name}.close() throws on invalid callback argument`, async function (t) { + t.plan(3 * 2) + + const db = testCommon.factory() + await db.open() + + for (const invalid of [{}, null, 'foo']) { + const it = new Ctor(db, {}) + + try { + it.close(invalid) + } catch (err) { + t.is(err.name, 'TypeError') + t.is(err.message, 'Callback must be a function') + } + } + }) +} + +test('AbstractIterator throws when accessing legacy properties', async function (t) { + t.plan(3 * 2) + + const db = testCommon.factory() + await db.open() + const it = new AbstractIterator(db, {}) + + for (const k of ['_ended property', '_nexting property', '_end method']) { + try { + // eslint-disable-next-line no-unused-expressions + it[k.split(' ')[0]] + } catch (err) { + t.is(err.code, 'LEVEL_LEGACY') + } + + try { + it[k.split(' ')[0]] = 123 + } catch (err) { + t.is(err.code, 'LEVEL_LEGACY') + } + } +}) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/async-iterator-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/async-iterator-test.js new file mode 100644 index 0000000..4455f62 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/async-iterator-test.js @@ -0,0 +1,213 @@ +'use strict' + +const test = require('tape') +const { AbstractLevel, AbstractIterator } = require('../..') +const { DeferredIterator, DeferredKeyIterator, DeferredValueIterator } = require('../../lib/deferred-iterator') + +function withIterator (methods) { + class TestIterator extends AbstractIterator { } + + for (const k in methods) { + TestIterator.prototype[k] = methods[k] + } + + class Test extends AbstractLevel { + _iterator (options) { + return new TestIterator(this, options) + } + } + + return new Test({ encodings: { utf8: true } }) +} + +for (const mode of ['iterator', 'keys', 'values']) { + for (const type of ['explicit', 'deferred']) { + const verify = function (t, db, it) { + t.is(db.status, type === 'explicit' ? 'open' : 'opening') + + if (type === 'explicit') { + t.is( + it.constructor.name, + mode === 'iterator' ? 'TestIterator' : mode === 'keys' ? 'DefaultKeyIterator' : 'DefaultValueIterator' + ) + } else { + t.is( + it.constructor, + mode === 'iterator' ? DeferredIterator : mode === 'keys' ? DeferredKeyIterator : DeferredValueIterator + ) + } + } + + test(`for await...of ${mode}() (${type} open)`, async function (t) { + t.plan(4) + + const input = [{ key: '1', value: '1' }, { key: '2', value: '2' }] + const output = [] + + const db = withIterator({ + _next (callback) { + const { key, value } = input[n++] || [] + this.nextTick(callback, null, key, value) + }, + + _close (callback) { + this.nextTick(function () { + closed = true + callback() + }) + } + }) + + if (type === 'explicit') await db.open() + const it = db[mode]({ keyEncoding: 'utf8', valueEncoding: 'utf8' }) + verify(t, db, it) + + let n = 0 + let closed = false + + for await (const item of it) { + output.push(item) + } + + t.same(output, input.map(x => mode === 'iterator' ? [x.key, x.value] : mode === 'keys' ? x.key : x.value)) + t.ok(closed, 'closed') + }) + + test(`for await...of ${mode}() closes on user error (${type} open)`, async function (t) { + t.plan(4) + + const db = withIterator({ + _next (callback) { + this.nextTick(callback, null, n.toString(), n.toString()) + if (n++ > 10) throw new Error('Infinite loop') + }, + + _close (callback) { + this.nextTick(function () { + closed = true + callback(new Error('close error')) + }) + } + }) + + if (type === 'explicit') await db.open() + const it = db[mode]() + verify(t, db, it) + + let n = 0 + let closed = false + + try { + // eslint-disable-next-line no-unused-vars, no-unreachable-loop + for await (const kv of it) { + throw new Error('user error') + } + } catch (err) { + t.is(err.message, 'user error') + t.ok(closed, 'closed') + } + }) + + test(`for await...of ${mode}() closes on iterator error (${type} open)`, async function (t) { + t.plan(5) + + const db = withIterator({ + _next (callback) { + t.pass('nexted') + this.nextTick(callback, new Error('iterator error')) + }, + + _close (callback) { + this.nextTick(function () { + closed = true + callback() + }) + } + }) + + if (type === 'explicit') await db.open() + const it = db[mode]() + verify(t, db, it) + + let closed = false + + try { + // eslint-disable-next-line no-unused-vars + for await (const kv of it) { + t.fail('should not yield items') + } + } catch (err) { + t.is(err.message, 'iterator error') + t.ok(closed, 'closed') + } + }) + + test(`for await...of ${mode}() closes on user break (${type} open)`, async function (t) { + t.plan(4) + + const db = withIterator({ + _next (callback) { + this.nextTick(callback, null, n.toString(), n.toString()) + if (n++ > 10) throw new Error('Infinite loop') + }, + + _close (callback) { + this.nextTick(function () { + closed = true + callback() + }) + } + }) + + if (type === 'explicit') await db.open() + const it = db[mode]() + verify(t, db, it) + + let n = 0 + let closed = false + + // eslint-disable-next-line no-unused-vars, no-unreachable-loop + for await (const kv of it) { + t.pass('got a chance to break') + break + } + + t.ok(closed, 'closed') + }) + + test(`for await...of ${mode}() closes on user return (${type} open)`, async function (t) { + t.plan(4) + + const db = withIterator({ + _next (callback) { + this.nextTick(callback, null, n.toString(), n.toString()) + if (n++ > 10) throw new Error('Infinite loop') + }, + + _close (callback) { + this.nextTick(function () { + closed = true + callback() + }) + } + }) + + if (type === 'explicit') await db.open() + const it = db[mode]() + verify(t, db, it) + + let n = 0 + let closed = false + + await (async () => { + // eslint-disable-next-line no-unused-vars, no-unreachable-loop + for await (const kv of it) { + t.pass('got a chance to return') + return + } + })() + + t.ok(closed, 'closed') + }) + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/attach-resource-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/attach-resource-test.js new file mode 100644 index 0000000..0961fcb --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/attach-resource-test.js @@ -0,0 +1,75 @@ +'use strict' + +const test = require('tape') +const { mockLevel } = require('../util') +const nextTick = require('../../lib/next-tick') + +test('resource must be an object with a close() method', function (t) { + t.plan(5) + + const db = mockLevel() + + for (const invalid of [null, undefined, {}, { close: 123 }]) { + try { + db.attachResource(invalid) + } catch (err) { + t.is(err && err.message, 'The first argument must be a resource object') + } + } + + db.close(t.ifError.bind(t)) +}) + +test('resource is closed on failed open', function (t) { + t.plan(2) + + const db = mockLevel({ + _open: function (options, callback) { + t.pass('opened') + this.nextTick(callback, new Error('_open error')) + } + }) + + const resource = { + close: function (cb) { + // Note: resource shouldn't care about db.status + t.pass('closed') + nextTick(cb) + } + } + + db.attachResource(resource) +}) + +test('resource is closed on db.close()', function (t) { + t.plan(2) + + const db = mockLevel() + + const resource = { + close: function (cb) { + // Note: resource shouldn't care about db.status + t.pass('closed') + nextTick(cb) + } + } + + db.attachResource(resource) + db.close(t.ifError.bind(t)) +}) + +test('resource is not closed on db.close() if detached', function (t) { + t.plan(1) + + const db = mockLevel() + + const resource = { + close: function (cb) { + t.fail('should not be called') + } + } + + db.attachResource(resource) + db.detachResource(resource) + db.close(t.ifError.bind(t)) +}) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/defer-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/defer-test.js new file mode 100644 index 0000000..8b8376a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/defer-test.js @@ -0,0 +1,90 @@ +'use strict' + +const test = require('tape') +const { mockLevel } = require('../util') + +test('defer() requires valid function argument', function (t) { + t.plan(7) + + const db = mockLevel() + + for (const invalid of [123, true, false, null, undefined, {}]) { + try { + db.defer(invalid) + } catch (err) { + t.is(err.message, 'The first argument must be a function') + } + } + + db.close(t.ifError.bind(t)) +}) + +test('defer() custom operation', function (t) { + t.plan(6) + + const db = mockLevel({ + custom (arg, callback) { + if (this.status === 'opening') { + t.is(arg, 123) + this.defer(() => this.custom(456, callback)) + } else { + t.is(db.status, 'open') + t.is(arg, 456) + this.nextTick(callback, null, 987) + } + } + }) + + db.custom(123, function (err, result) { + t.ifError(err, 'no custom() error') + t.is(result, 987, 'result ok') + + db.close(t.ifError.bind(t)) + }) +}) + +test('defer() custom operation with failed open', function (t) { + t.plan(4) + + const db = mockLevel({ + _open (options, callback) { + t.pass('opened') + this.nextTick(callback, new Error('_open error')) + }, + custom (arg, callback) { + if (this.status === 'opening') { + this.defer(() => this.custom(arg, callback)) + } else { + t.is(db.status, 'closed') + this.nextTick(callback, new Error('Database is not open (x)')) + } + } + }) + + db.custom(123, function (err, result) { + t.is(err && err.message, 'Database is not open (x)') + t.is(result, undefined, 'result ok') + }) +}) + +test('defer() can drop custom synchronous operation', function (t) { + t.plan(3) + + const db = mockLevel({ + _open (options, callback) { + t.pass('opened') + this.nextTick(callback, new Error('_open error')) + }, + custom (arg) { + if (this.status === 'opening') { + this.defer(() => this.custom(arg * 2)) + } else { + // Handling other states is a userland responsibility + t.is(db.status, 'closed') + t.is(arg, 246) + } + } + }) + + db.custom(123) +}) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/deferred-chained-batch-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/deferred-chained-batch-test.js new file mode 100644 index 0000000..bbf26ea --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/deferred-chained-batch-test.js @@ -0,0 +1,98 @@ +'use strict' + +const test = require('tape') +const { mockLevel } = require('../util') +const { DefaultChainedBatch } = require('../../lib/default-chained-batch') +const identity = (v) => v + +// NOTE: adapted from deferred-leveldown +test('deferred chained batch encodes once', function (t) { + t.plan(9) + + let called = false + + const keyEncoding = { + format: 'utf8', + encode (key) { + t.is(called, false, 'not yet called') + t.is(key, 'foo') + return key.toUpperCase() + }, + decode: identity + } + + const valueEncoding = { + format: 'utf8', + encode (value) { + t.is(called, false, 'not yet called') + t.is(value, 'bar') + return value.toUpperCase() + }, + decode: identity + } + + const db = mockLevel({ + _batch: function (array, options, callback) { + called = true + t.is(array[0] && array[0].key, 'FOO') + t.is(array[0] && array[0].value, 'BAR') + this.nextTick(callback) + }, + _open: function (options, callback) { + t.is(called, false, 'not yet called') + this.nextTick(callback) + } + }, { encodings: { utf8: true } }, { + keyEncoding, + valueEncoding + }) + + db.once('open', function () { + t.is(called, true, 'called') + }) + + db.batch().put('foo', 'bar').write(function (err) { + t.ifError(err, 'no write() error') + }) +}) + +test('deferred chained batch is closed upon failed open', function (t) { + t.plan(6) + + const db = mockLevel({ + _open (options, callback) { + t.pass('opening') + this.nextTick(callback, new Error('_open error')) + }, + _batch () { + t.fail('should not be called') + } + }) + + const batch = db.batch() + t.ok(batch instanceof DefaultChainedBatch) + + batch.put('foo', 'bar') + batch.del('123') + + batch.write(function (err) { + t.is(err && err.code, 'LEVEL_BATCH_NOT_OPEN') + + // Should account for userland code that ignores errors + try { + batch.put('beep', 'boop') + } catch (err) { + t.is(err && err.code, 'LEVEL_BATCH_NOT_OPEN') + } + + try { + batch.del('456') + } catch (err) { + t.is(err && err.code, 'LEVEL_BATCH_NOT_OPEN') + } + + batch.write(function (err) { + t.is(err && err.code, 'LEVEL_BATCH_NOT_OPEN') + }) + }) +}) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/deferred-iterator-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/deferred-iterator-test.js new file mode 100644 index 0000000..c88c561 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/deferred-iterator-test.js @@ -0,0 +1,298 @@ +'use strict' + +const test = require('tape') +const { DeferredIterator, DeferredKeyIterator, DeferredValueIterator } = require('../../lib/deferred-iterator') +const { AbstractIterator, AbstractKeyIterator, AbstractValueIterator } = require('../..') +const { mockLevel } = require('../util') +const noop = () => {} +const identity = (v) => v + +for (const mode of ['iterator', 'keys', 'values']) { + const RealCtor = mode === 'iterator' ? AbstractIterator : mode === 'keys' ? AbstractKeyIterator : AbstractValueIterator + const DeferredCtor = mode === 'iterator' ? DeferredIterator : mode === 'keys' ? DeferredKeyIterator : DeferredValueIterator + const nextArgs = mode === 'iterator' ? ['key', 'value'] : mode === 'keys' ? ['key'] : ['value'] + const privateMethod = '_' + mode + const publicMethod = mode + + // NOTE: adapted from deferred-leveldown + test(`deferred ${mode}()`, function (t) { + t.plan(8) + + const keyEncoding = { + format: 'utf8', + encode (key) { + t.is(key, 'foo', 'encoding got key') + return key.toUpperCase() + }, + decode: identity + } + + class MockIterator extends RealCtor { + _next (cb) { + this.nextTick(cb, null, ...nextArgs) + } + + _close (cb) { + this.nextTick(cb) + } + } + + const db = mockLevel({ + [privateMethod]: function (options) { + t.is(options.gt, 'FOO', 'got encoded range option') + return new MockIterator(this, options) + }, + _open: function (options, callback) { + t.pass('opened') + this.nextTick(callback) + } + }, { encodings: { utf8: true } }, { + keyEncoding + }) + + const it = db[publicMethod]({ gt: 'foo' }) + t.ok(it instanceof DeferredCtor, 'is deferred') + + let nextFirst = false + + it.next(function (err, ...rest) { + nextFirst = true + t.error(err, 'no next() error') + t.same(rest, nextArgs) + }) + + it.close(function (err) { + t.error(err, 'no close() error') + t.ok(nextFirst) + }) + }) + + // NOTE: adapted from deferred-leveldown + test(`deferred ${mode}(): non-deferred operations`, function (t) { + t.plan(6) + + class MockIterator extends RealCtor { + _seek (target) { + t.is(target, '123') + } + + _next (cb) { + this.nextTick(cb, null, ...nextArgs) + } + } + + const db = mockLevel({ + [privateMethod]: function (options) { + return new MockIterator(this, options) + } + }) + + db.open(function (err) { + t.error(err, 'no open() error') + + it.seek(123) + it.next(function (err, ...rest) { + t.error(err, 'no next() error') + t.same(rest, nextArgs) + + it.close(function (err) { + t.error(err, 'no close() error') + }) + }) + }) + + const it = db[publicMethod]({ gt: 'foo' }) + t.ok(it instanceof DeferredCtor) + }) + + // NOTE: adapted from deferred-leveldown + test(`deferred ${mode}(): iterators are created in order`, function (t) { + t.plan(6) + + const order1 = [] + const order2 = [] + + class MockIterator extends RealCtor {} + + function db (order) { + return mockLevel({ + [privateMethod]: function (options) { + order.push('iterator created') + return new MockIterator(this, options) + }, + _put: function (key, value, options, callback) { + order.push('put') + }, + _open: function (options, callback) { + this.nextTick(callback) + } + }) + } + + const db1 = db(order1) + const db2 = db(order2) + + db1.open(function (err) { + t.error(err, 'no error') + t.same(order1, ['iterator created', 'put']) + }) + + db2.open(function (err) { + t.error(err, 'no error') + t.same(order2, ['put', 'iterator created']) + }) + + t.ok(db1[publicMethod]() instanceof DeferredCtor) + db1.put('key', 'value', noop) + + db2.put('key', 'value', noop) + t.ok(db2[publicMethod]() instanceof DeferredCtor) + }) + + for (const method of ['next', 'nextv', 'all']) { + test(`deferred ${mode}(): closed upon failed open, verified by ${method}()`, function (t) { + t.plan(5) + + const db = mockLevel({ + _open (options, callback) { + t.pass('opening') + this.nextTick(callback, new Error('_open error')) + }, + _iterator () { + t.fail('should not be called') + }, + [privateMethod] () { + t.fail('should not be called') + } + }) + + const it = db[publicMethod]() + t.ok(it instanceof DeferredCtor) + + const original = it._close + it._close = function (...args) { + t.pass('closed') + return original.call(this, ...args) + } + + verifyClosed(t, it, method, () => {}) + }) + + test(`deferred ${mode}(): deferred and real iterators are closed on db.close(), verified by ${method}()`, function (t) { + t.plan(10) + + class MockIterator extends RealCtor { + _close (callback) { + t.pass('closed') + this.nextTick(callback) + } + } + + const db = mockLevel({ + [privateMethod] (options) { + return new MockIterator(this, options) + } + }) + + const it = db[publicMethod]() + t.ok(it instanceof DeferredCtor) + + const original = it._close + it._close = function (...args) { + t.pass('closed') + return original.call(this, ...args) + } + + db.close(function (err) { + t.ifError(err, 'no close() error') + + verifyClosed(t, it, method, function () { + db.open(function (err) { + t.ifError(err, 'no open() error') + + // Should still be closed + verifyClosed(t, it, method, function () { + db.close(t.ifError.bind(t)) + }) + }) + }) + }) + }) + } + + test(`deferred ${mode}(): deferred and real iterators are detached on db.close()`, function (t) { + t.plan(4) + + class MockIterator extends RealCtor {} + + let real + const db = mockLevel({ + [privateMethod] (options) { + real = new MockIterator(this, options) + return real + } + }) + + const it = db[publicMethod]() + t.ok(it instanceof DeferredCtor) + + db.close(function (err) { + t.ifError(err, 'no close() error') + + db.open(function (err) { + t.ifError(err, 'no open() error') + + it.close = real.close = it._close = real._close = function () { + t.fail('should not be called') + } + + db.close(t.ifError.bind(t)) + }) + }) + }) + + test(`deferred ${mode}(): defers underlying close()`, function (t) { + t.plan(3) + + class MockIterator extends RealCtor { + _close (callback) { + order.push('_close') + this.nextTick(callback) + } + } + + const order = [] + const db = mockLevel({ + _open (options, callback) { + order.push('_open') + this.nextTick(callback) + }, + [privateMethod] (options) { + order.push(privateMethod) + return new MockIterator(this, options) + } + }) + + const it = db[publicMethod]() + t.ok(it instanceof DeferredCtor) + + it.close(function (err) { + t.ifError(err, 'no close() error') + t.same(order, ['_open', privateMethod, '_close']) + }) + }) + + const verifyClosed = function (t, it, method, cb) { + const requiredArgs = method === 'nextv' ? [10] : [] + + it[method](...requiredArgs, function (err) { + t.is(err && err.code, 'LEVEL_ITERATOR_NOT_OPEN', `correct error on first ${method}()`) + + // Should account for userland code that ignores errors + it[method](...requiredArgs, function (err) { + t.is(err && err.code, 'LEVEL_ITERATOR_NOT_OPEN', `correct error on second ${method}()`) + cb() + }) + }) + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/deferred-operations-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/deferred-operations-test.js new file mode 100644 index 0000000..e1d0f2b --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/deferred-operations-test.js @@ -0,0 +1,93 @@ +'use strict' + +const test = require('tape') +const { mockLevel, mockIterator } = require('../util') + +// NOTE: copied from deferred-leveldown +test('deferred operations are called in order', function (t) { + t.plan(15) + + const calls = [] + const db = mockLevel({ + _put: function (key, value, options, callback) { + this.nextTick(callback) + calls.push({ type: 'put', key, value, options }) + }, + _get: function (key, options, callback) { + this.nextTick(callback) + calls.push({ type: 'get', key, options }) + }, + _del: function (key, options, callback) { + this.nextTick(callback) + calls.push({ type: 'del', key, options }) + }, + _batch: function (arr, options, callback) { + this.nextTick(callback) + calls.push({ type: 'batch', keys: arr.map(op => op.key).join(',') }) + }, + _clear: function (options, callback) { + this.nextTick(callback) + calls.push({ ...options, type: 'clear' }) + }, + _iterator (options) { + calls.push({ type: 'iterator' }) + return mockIterator(this, options, { + _next (callback) { + this.nextTick(callback) + calls.push({ type: 'iterator.next' }) + } + }) + }, + _open: function (options, callback) { + this.nextTick(callback) + t.is(calls.length, 0, 'not yet called') + } + }, { + encodings: { + utf8: true, + buffer: true + } + }, { + keyEncoding: 'utf8', + valueEncoding: 'utf8' + }) + + db.open(function (err) { + t.ifError(err, 'no open() error') + t.same(calls, [ + { type: 'put', key: '001', value: 'bar1', options: { keyEncoding: 'utf8', valueEncoding: 'utf8' } }, + { type: 'get', key: '002', options: { keyEncoding: 'utf8', valueEncoding: 'utf8' } }, + { type: 'clear', reverse: false, limit: -1, keyEncoding: 'utf8' }, + { type: 'put', key: '010', value: 'bar2', options: { keyEncoding: 'utf8', valueEncoding: 'utf8' } }, + { type: 'get', key: Buffer.from('011'), options: { keyEncoding: 'buffer', valueEncoding: 'utf8' } }, + { type: 'del', key: '020', options: { customOption: 123, keyEncoding: 'utf8' } }, + { type: 'del', key: '021', options: { keyEncoding: 'utf8' } }, + { type: 'batch', keys: '040,041' }, + { type: 'iterator' }, + { type: 'batch', keys: '050,051' }, + { type: 'iterator.next' }, + { type: 'clear', gt: '060', reverse: false, limit: -1, keyEncoding: 'utf8' } + ], 'calls correctly behaved') + }) + + db.put('001', 'bar1', t.ifError.bind(t)) + db.get('002', t.ifError.bind(t)) + db.clear(t.ifError.bind(t)) + db.put('010', 'bar2', t.ifError.bind(t)) + db.get('011', { keyEncoding: 'buffer' }, t.ifError.bind(t)) + db.del('020', { customOption: 123 }, t.ifError.bind(t)) + db.del('021', t.ifError.bind(t)) + db.batch([ + { type: 'put', key: '040', value: 'a' }, + { type: 'put', key: '041', value: 'b' } + ], t.ifError.bind(t)) + const it = db.iterator() + db.batch() + .put('050', 'c') + .put('051', 'd') + .write(t.ifError.bind(t)) + it.next(t.ifError.bind(t)) + db.clear({ gt: '060' }, t.ifError.bind(t)) + + t.is(calls.length, 0, 'not yet called') +}) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/encoding-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/encoding-test.js new file mode 100644 index 0000000..dd381f6 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/encoding-test.js @@ -0,0 +1,426 @@ +'use strict' + +// TODO: move to per-method test files + +const test = require('tape') +const { Buffer } = require('buffer') +const { mockLevel, mockChainedBatch, nullishEncoding } = require('../util') +const identity = (v) => v + +const utf8Manifest = { encodings: { utf8: true } } +const dualManifest = { encodings: { utf8: true, buffer: true } } +const hasOwnProperty = Object.prototype.hasOwnProperty + +for (const deferred of [false, true]) { + // NOTE: adapted from encoding-down + test(`get() encodes utf8 key (deferred: ${deferred})`, async function (t) { + t.plan(4) + + const db = mockLevel({ + _get (key, options, callback) { + t.is(key, '8') + t.is(options.keyEncoding, 'utf8') + t.is(options.valueEncoding, 'utf8') + this.nextTick(callback, null, 'foo') + } + }, utf8Manifest) + + if (!deferred) await db.open() + t.same(await db.get(8), 'foo') + }) + + // NOTE: adapted from encoding-down + test(`get() takes encoding options (deferred: ${deferred})`, async function (t) { + t.plan(4) + + const db = mockLevel({ + _get (key, options, callback) { + t.is(key, '[1,"2"]') + t.is(options.keyEncoding, 'utf8') + t.is(options.valueEncoding, 'utf8') + this.nextTick(callback, null, '123') + } + }, utf8Manifest) + + if (!deferred) await db.open() + t.same(await db.get([1, '2'], { keyEncoding: 'json', valueEncoding: 'json' }), 123) + }) + + // NOTE: adapted from encoding-down + test(`get() with custom value encoding that wants a buffer (deferred: ${deferred})`, async function (t) { + t.plan(3) + + const db = mockLevel({ + _get (key, options, callback) { + t.same(key, 'key') + t.same(options, { keyEncoding: 'utf8', valueEncoding: 'buffer' }) + this.nextTick(callback, null, Buffer.alloc(1)) + } + }, dualManifest, { + keyEncoding: 'utf8', + valueEncoding: { encode: identity, decode: identity, format: 'buffer' } + }) + + if (!deferred) await db.open() + t.same(await db.get('key'), Buffer.alloc(1)) + }) + + // NOTE: adapted from encoding-down + test(`get() with custom value encoding that wants a string (deferred: ${deferred})`, async function (t) { + t.plan(3) + + const db = mockLevel({ + _get (key, options, callback) { + t.same(key, Buffer.from('key')) + t.same(options, { keyEncoding: 'buffer', valueEncoding: 'utf8' }) + this.nextTick(callback, null, 'x') + } + }, dualManifest, { + keyEncoding: 'buffer', + valueEncoding: { encode: identity, decode: identity, format: 'utf8' } + }) + + if (!deferred) await db.open() + t.same(await db.get(Buffer.from('key')), 'x') + }) + + // NOTE: adapted from encoding-down + test(`put() encodes utf8 key and value (deferred: ${deferred})`, async function (t) { + t.plan(4) + + const db = mockLevel({ + _put (key, value, options, callback) { + t.is(key, '8') + t.is(value, '4') + t.is(options.keyEncoding, 'utf8') + t.is(options.valueEncoding, 'utf8') + this.nextTick(callback) + } + }, utf8Manifest) + + if (!deferred) await db.open() + await db.put(8, 4) + }) + + // NOTE: adapted from encoding-down + test(`put() takes encoding options (deferred: ${deferred})`, async function (t) { + t.plan(4) + + const db = mockLevel({ + _put (key, value, options, callback) { + t.is(key, '[1,"2"]') + t.is(value, '{"x":3}') + t.is(options.keyEncoding, 'utf8') + t.is(options.valueEncoding, 'utf8') + this.nextTick(callback) + } + }, utf8Manifest) + + if (!deferred) await db.open() + await db.put([1, '2'], { x: 3 }, { keyEncoding: 'json', valueEncoding: 'json' }) + }) + + // NOTE: adapted from encoding-down + test(`del() encodes utf8 key (deferred: ${deferred})`, async function (t) { + t.plan(2) + + const db = mockLevel({ + _del (key, options, callback) { + t.is(key, '2') + t.is(options.keyEncoding, 'utf8') + this.nextTick(callback) + } + }, utf8Manifest) + + if (!deferred) await db.open() + await db.del(2) + }) + + // NOTE: adapted from encoding-down + test(`del() takes keyEncoding option (deferred: ${deferred})`, async function (t) { + t.plan(2) + + const db = mockLevel({ + _del (key, options, callback) { + t.is(key, '[1,"2"]') + t.is(options.keyEncoding, 'utf8') + this.nextTick(callback) + } + }, utf8Manifest) + + if (!deferred) await db.open() + await db.del([1, '2'], { keyEncoding: 'json' }) + }) + + test(`getMany() encodes utf8 key (deferred: ${deferred})`, async function (t) { + t.plan(4) + + const db = mockLevel({ + _getMany (keys, options, callback) { + t.same(keys, ['8', '29']) + t.is(options.keyEncoding, 'utf8') + t.is(options.valueEncoding, 'utf8') + this.nextTick(callback, null, ['foo', 'bar']) + } + }, utf8Manifest) + + if (!deferred) await db.open() + t.same(await db.getMany([8, 29]), ['foo', 'bar']) + }) + + test(`getMany() takes encoding options (deferred: ${deferred})`, async function (t) { + t.plan(4) + + const db = mockLevel({ + _getMany (keys, options, callback) { + t.same(keys, ['[1,"2"]', '"x"']) + t.is(options.keyEncoding, 'utf8') + t.is(options.valueEncoding, 'utf8') + this.nextTick(callback, null, ['123', '"hi"']) + } + }, utf8Manifest) + + if (!deferred) await db.open() + t.same(await db.getMany([[1, '2'], 'x'], { keyEncoding: 'json', valueEncoding: 'json' }), [123, 'hi']) + }) + + test(`getMany() with custom value encoding that wants a buffer (deferred: ${deferred})`, async function (t) { + t.plan(3) + + const db = mockLevel({ + _getMany (keys, options, callback) { + t.same(keys, ['key']) + t.same(options, { keyEncoding: 'utf8', valueEncoding: 'buffer' }) + this.nextTick(callback, null, [Buffer.alloc(1)]) + } + }, dualManifest, { + keyEncoding: 'utf8', + valueEncoding: { encode: identity, decode: identity, format: 'buffer' } + }) + + if (!deferred) await db.open() + t.same(await db.getMany(['key']), [Buffer.alloc(1)]) + }) + + test(`getMany() with custom value encoding that wants a string (deferred: ${deferred})`, async function (t) { + t.plan(3) + + const db = mockLevel({ + _getMany (keys, options, callback) { + t.same(keys, [Buffer.from('key')]) + t.same(options, { keyEncoding: 'buffer', valueEncoding: 'utf8' }) + this.nextTick(callback, null, ['x']) + } + }, dualManifest, { + keyEncoding: 'buffer', + valueEncoding: { encode: identity, decode: identity, format: 'utf8' } + }) + + if (!deferred) await db.open() + t.same(await db.getMany([Buffer.from('key')]), ['x']) + }) + + // NOTE: adapted from encoding-down + test(`chainedBatch.put() and del() encode utf8 key and value (deferred: ${deferred})`, async function (t) { + t.plan(deferred ? 2 : 4) + + let db + + if (deferred) { + db = mockLevel({ + _batch (array, options, callback) { + t.same(array, [ + { type: 'put', key: '1', value: '2', keyEncoding: 'utf8', valueEncoding: 'utf8' }, + { type: 'del', key: '3', keyEncoding: 'utf8' } + ]) + t.same(options, {}) + this.nextTick(callback) + } + }, utf8Manifest) + } else { + db = mockLevel({ + _chainedBatch () { + return mockChainedBatch(this, { + _put: function (key, value, options) { + t.same({ key, value }, { key: '1', value: '2' }) + t.same(options, { keyEncoding: 'utf8', valueEncoding: 'utf8' }) + }, + _del: function (key, options) { + t.is(key, '3') + t.same(options, { keyEncoding: 'utf8' }) + } + }) + } + }, utf8Manifest) + } + + if (!deferred) await db.open() + await db.batch().put(1, 2).del(3).write() + }) + + // NOTE: adapted from encoding-down + test(`chainedBatch.put() and del() take encoding options (deferred: ${deferred})`, async function (t) { + t.plan(deferred ? 2 : 4) + + let db + + const putOptions = { keyEncoding: 'json', valueEncoding: 'json' } + const delOptions = { keyEncoding: 'json' } + + if (deferred) { + db = mockLevel({ + _batch (array, options, callback) { + t.same(array, [ + { type: 'put', key: '"1"', value: '{"x":[2]}', keyEncoding: 'utf8', valueEncoding: 'utf8' }, + { type: 'del', key: '"3"', keyEncoding: 'utf8' } + ]) + t.same(options, {}) + this.nextTick(callback) + } + }, utf8Manifest) + } else { + db = mockLevel({ + _chainedBatch () { + return mockChainedBatch(this, { + _put: function (key, value, options) { + t.same({ key, value }, { key: '"1"', value: '{"x":[2]}' }) + t.same(options, { keyEncoding: 'utf8', valueEncoding: 'utf8' }) + }, + _del: function (key, options) { + t.is(key, '"3"') + t.same(options, { keyEncoding: 'utf8' }) + } + }) + } + }, utf8Manifest) + } + + if (!deferred) await db.open() + await db.batch().put('1', { x: [2] }, putOptions).del('3', delOptions).write() + }) + + // NOTE: adapted from encoding-down + test(`clear() receives keyEncoding option (deferred: ${deferred})`, async function (t) { + t.plan(1) + + const db = mockLevel({ + _clear: function (options, callback) { + t.same(options, { keyEncoding: 'utf8', reverse: false, limit: -1 }) + this.nextTick(callback) + } + }, utf8Manifest) + + if (!deferred) await db.open() + await db.clear() + }) + + test(`clear() takes keyEncoding option (deferred: ${deferred})`, async function (t) { + t.plan(1) + + const db = mockLevel({ + _clear: function (options, callback) { + t.same(options, { keyEncoding: 'utf8', gt: '"a"', reverse: false, limit: -1 }) + this.nextTick(callback) + } + }, utf8Manifest) + + if (!deferred) await db.open() + await db.clear({ keyEncoding: 'json', gt: 'a' }) + }) + + // NOTE: adapted from encoding-down + test(`clear() encodes range options (deferred: ${deferred})`, async function (t) { + t.plan(5) + + const keyEncoding = { + format: 'utf8', + encode: function (key) { + return 'encoded_' + key + }, + decode: identity + } + + const db = mockLevel({ + _clear: function (options, callback) { + t.is(options.gt, 'encoded_1') + t.is(options.gte, 'encoded_2') + t.is(options.lt, 'encoded_3') + t.is(options.lte, 'encoded_4') + t.is(options.foo, 5) + this.nextTick(callback) + } + }, utf8Manifest, { keyEncoding }) + + if (!deferred) await db.open() + await db.clear({ gt: 1, gte: 2, lt: 3, lte: 4, foo: 5 }) + }) + + // NOTE: adapted from encoding-down + test(`clear() does not strip nullish range options (deferred: ${deferred})`, async function (t) { + t.plan(12) + + const db1 = mockLevel({ + _clear: function (options, callback) { + t.is(options.gt, '\x00', 'encoded null') + t.is(options.gte, '\x00', 'encoded null') + t.is(options.lt, '\x00', 'encoded null') + t.is(options.lte, '\x00', 'encoded null') + this.nextTick(callback) + } + }, utf8Manifest, { keyEncoding: nullishEncoding, valueEncoding: nullishEncoding }) + + const db2 = mockLevel({ + _clear: function (options, callback) { + t.is(hasOwnProperty.call(options, 'gt'), true) + t.is(hasOwnProperty.call(options, 'gte'), true) + t.is(hasOwnProperty.call(options, 'lt'), true) + t.is(hasOwnProperty.call(options, 'lte'), true) + + t.is(options.gt, '\xff', 'encoded undefined') + t.is(options.gte, '\xff', 'encoded undefined') + t.is(options.lt, '\xff', 'encoded undefined') + t.is(options.lte, '\xff', 'encoded undefined') + + this.nextTick(callback) + } + }, utf8Manifest, { keyEncoding: nullishEncoding, valueEncoding: nullishEncoding }) + + if (!deferred) { + await Promise.all([db1.open(), db2.open()]) + } + + const promise1 = db1.clear({ + gt: null, + gte: null, + lt: null, + lte: null + }) + + const promise2 = db2.clear({ + gt: undefined, + gte: undefined, + lt: undefined, + lte: undefined + }) + + await Promise.all([promise1, promise2]) + }) + + // NOTE: adapted from encoding-down + test(`clear() does not add nullish range options (deferred: ${deferred})`, async function (t) { + t.plan(4) + + const db = mockLevel({ + _clear: function (options, callback) { + t.is(hasOwnProperty.call(options, 'gt'), false) + t.is(hasOwnProperty.call(options, 'gte'), false) + t.is(hasOwnProperty.call(options, 'lt'), false) + t.is(hasOwnProperty.call(options, 'lte'), false) + this.nextTick(callback) + } + }) + + if (!deferred) await db.open() + await db.clear({}) + }) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/iterator-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/iterator-test.js new file mode 100644 index 0000000..979b005 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/iterator-test.js @@ -0,0 +1,829 @@ +'use strict' + +const test = require('tape') +const { Buffer } = require('buffer') +const { AbstractLevel } = require('../..') +const { AbstractIterator, AbstractKeyIterator, AbstractValueIterator } = require('../..') +const { mockLevel, mockIterator, nullishEncoding } = require('../util') + +const identity = (v) => v +const utf8Manifest = { encodings: { utf8: true } } +const dualManifest = { encodings: { utf8: true, buffer: true } } +const tripleManifest = { encodings: { utf8: true, buffer: true, view: true } } + +for (const deferred of [false, true]) { + // Also test default fallback implementations of keys() and values() + for (const [mode, def] of [['iterator', false], ['keys', false], ['values', false], ['keys', true], ['values', true]]) { + const Ctor = mode === 'iterator' || def ? AbstractIterator : mode === 'keys' ? AbstractKeyIterator : AbstractValueIterator + const privateMethod = def ? '_iterator' : '_' + mode + const publicMethod = mode + + test(`${mode}() (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(4) + + let called = false + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + t.is(this, db, 'thisArg is correct') + t.is(arguments.length, 1, 'got one argument') + + const kvOptions = mode === 'iterator' || def + ? { keys: mode !== 'values', values: mode !== 'keys' } + : {} + + t.same(options, { + reverse: false, + limit: -1, + keyEncoding: 'utf8', + valueEncoding: 'utf8', + ...kvOptions + }) + + called = true + return new Ctor(this, options) + } + } + + const db = new MockLevel(tripleManifest) + if (!deferred) await db.open() + + db[publicMethod]() + t.is(called, !deferred) + if (deferred) await db.open() + }) + + test(`${mode}() with custom options (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(3) + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + t.is(options.foo, 123) + t.is(options.reverse, true) + t.is(options.limit, 1) + + return new Ctor(this, options) + } + } + + const db = new MockLevel(tripleManifest) + if (!deferred) await db.open() + db[publicMethod]({ foo: 123, reverse: true, limit: 1 }) + if (deferred) await db.open() + }) + + for (const limit of [2, 0]) { + test(`${mode}().next() skips _next() when limit ${limit} is reached (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + return new MockIterator(this, options) + } + } + + let calls = 0 + let yielded = 0 + + class MockIterator extends Ctor { + _next (callback) { + calls++ + + if (mode === 'iterator' || def) { + this.nextTick(callback, null, 'a', 'a') + } else { + this.nextTick(callback, null, 'a') + } + } + } + + const db = new MockLevel(utf8Manifest) + if (!deferred) await db.open() + + const it = db[publicMethod]({ limit }) + + for (let i = 0; i < limit + 2; i++) { + const item = await it.next() + if (item === undefined) break + yielded++ + } + + t.is(it.count, limit, 'final count matches limit') + t.is(calls, limit) + t.is(yielded, limit) + }) + + test(`${mode}().nextv() skips _nextv() when limit ${limit} is reached (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + return new MockIterator(this, options) + } + } + + let calls = 0 + let yielded = 0 + + class MockIterator extends Ctor { + _nextv (size, options, callback) { + calls++ + + if (mode === 'iterator' || def) { + this.nextTick(callback, null, [['a', 'a']]) + } else { + this.nextTick(callback, null, ['a']) + } + } + } + + const db = new MockLevel(utf8Manifest) + if (!deferred) await db.open() + + const it = db[publicMethod]({ limit }) + + for (let i = 0; i < limit + 2; i++) { + const items = await it.nextv(1) + yielded += items.length + if (items.length === 0) break + } + + t.is(it.count, limit, 'final count matches limit') + t.is(calls, limit) + t.is(yielded, limit) + }) + + test(`${mode}().all() skips _all() when limit ${limit} is reached (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + return new MockIterator(this, options) + } + } + + let nextCount = 0 + class MockIterator extends Ctor { + _next (callback) { + if (++nextCount > 10) { + throw new Error('Potential infinite loop') + } else if (mode === 'iterator' || def) { + this.nextTick(callback, null, 'a', 'a') + } else { + this.nextTick(callback, null, 'a') + } + } + + _all (options, callback) { + t.fail('should not be called') + } + } + + const db = new MockLevel(utf8Manifest) + if (!deferred) await db.open() + + const it = db[publicMethod]({ limit }) + + // Use next() because all() auto-closes and thus can't be used twice atm + for (let i = 0; i < limit; i++) await it.next() + + t.same(await it.all(), []) + }) + } + + test(`${mode}().nextv() reduces size for _nextv() when near limit (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + return new MockIterator(this, options) + } + } + + class MockIterator extends Ctor { + _nextv (size, options, callback) { + if (mode === 'iterator' || def) { + this.nextTick(callback, null, Array(size).fill(['a', 'a'])) + } else { + this.nextTick(callback, null, Array(size).fill('a')) + } + } + } + + const db = new MockLevel(utf8Manifest) + if (!deferred) await db.open() + + const it = db[publicMethod]({ limit: 3 }) + + t.is((await it.nextv(2)).length, 2) + t.is((await it.nextv(2)).length, 1) + t.is((await it.nextv(2)).length, 0) + }) + + test(`${mode}().count increments by next(), nextv() and all() (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + return new MockIterator(this, options) + } + } + + class MockIterator extends Ctor { + _next (callback) { + if (mode === 'iterator' || def) { + this.nextTick(callback, null, 'a', 'a') + } else { + this.nextTick(callback, null, 'a') + } + } + + _nextv (size, options, callback) { + if (mode === 'iterator' || def) { + this.nextTick(callback, null, [['a', 'a'], ['b', 'b']]) + } else { + this.nextTick(callback, null, ['a', 'b']) + } + } + + _all (options, callback) { + if (mode === 'iterator' || def) { + this.nextTick(callback, null, [['c', 'c'], ['d', 'd'], ['e', 'e']]) + } else { + this.nextTick(callback, null, ['c', 'd', 'e']) + } + } + } + + const db = new MockLevel(utf8Manifest) + if (!deferred) await db.open() + + const it = db[publicMethod]() + + for (let i = 0; i < 2; i++) { + t.isNot(await it.next(), undefined) // 2 * 1 = 2 + t.is((await it.nextv(2)).length, 2) // 2 * 2 = 4 + } + + t.is(it.count, 2 + 4) + t.is((await it.all()).length, 3) + t.is(it.count, 2 + 4 + 3) + }) + + test(`${mode}() forwards encoding options (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(3) + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + t.is(options.keyEncoding, 'utf8') + t.is(options.valueEncoding, 'buffer') + + return new MockIterator(this, options) + } + } + + class MockIterator extends Ctor { + _next (callback) { + if (mode === 'iterator' || def) { + this.nextTick(callback, null, '281', Buffer.from('a')) + } else if (mode === 'keys') { + this.nextTick(callback, null, '281') + } else { + this.nextTick(callback, null, Buffer.from('a')) + } + } + } + + const db = new MockLevel(dualManifest) + if (!deferred) await db.open() + + const item = await db[publicMethod]({ keyEncoding: 'json', valueEncoding: 'hex' }).next() + t.same(item, mode === 'iterator' ? [281, '61'] : mode === 'keys' ? 281 : '61') + }) + + // NOTE: adapted from encoding-down + test(`${mode}() with custom encodings that want a buffer (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(5) + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + t.is(options.keyEncoding, 'buffer') + t.is(options.valueEncoding, 'buffer') + + return new MockIterator(this, options) + } + } + + class MockIterator extends Ctor { + _next (callback) { + if (mode === 'iterator' || def) { + this.nextTick(callback, null, Buffer.from('a'), Buffer.from('b')) + } else if (mode === 'keys') { + this.nextTick(callback, null, Buffer.from('a')) + } else { + this.nextTick(callback, null, Buffer.from('b')) + } + } + } + + const db = new MockLevel(dualManifest) + const encoding = { encode: spy(identity), decode: spy(identity), format: 'buffer' } + if (!deferred) await db.open() + + const it = db[publicMethod]({ keyEncoding: encoding, valueEncoding: encoding }) + const item = await it.next() + + t.is(encoding.encode.calls, 0, 'did not need to encode anything') + t.is(encoding.decode.calls, mode === 'iterator' ? 2 : 1) + t.same(item, mode === 'iterator' ? [Buffer.from('a'), Buffer.from('b')] : Buffer.from(mode === 'keys' ? 'a' : 'b')) + }) + + // NOTE: adapted from encoding-down + test(`${mode}() with custom encodings that want a string (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(5) + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + t.is(options.keyEncoding, 'utf8') + t.is(options.valueEncoding, 'utf8') + + return new MockIterator(this, options) + } + } + + class MockIterator extends Ctor { + _next (callback) { + if (mode === 'iterator' || def) { + this.nextTick(callback, null, 'a', 'b') + } else if (mode === 'keys') { + this.nextTick(callback, null, 'a') + } else { + this.nextTick(callback, null, 'b') + } + } + } + + const db = new MockLevel(dualManifest) + const encoding = { encode: spy(identity), decode: spy(identity), format: 'utf8' } + if (!deferred) await db.open() + + const it = db[publicMethod]({ keyEncoding: encoding, valueEncoding: encoding }) + const item = await it.next() + + t.is(encoding.encode.calls, 0, 'did not need to encode anything') + t.is(encoding.decode.calls, mode === 'iterator' ? 2 : 1) + t.same(item, mode === 'iterator' ? ['a', 'b'] : mode === 'keys' ? 'a' : 'b') + }) + + // NOTE: adapted from encoding-down + test(`${mode}() encodes range options (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(6) + + let calls = 0 + const keyEncoding = { + format: 'utf8', + encode (key) { + calls++ + return 'encoded_' + key + }, + decode: identity + } + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + t.is(options.gt, 'encoded_3') + t.is(options.gte, 'encoded_4') + t.is(options.lt, 'encoded_5') + t.is(options.lte, 'encoded_6') + t.is(options.foo, 7) + return new Ctor(this, options) + } + } + + const db = new MockLevel(utf8Manifest, { keyEncoding }) + if (!deferred) await db.open() + await db[publicMethod]({ gt: 3, gte: 4, lt: 5, lte: 6, foo: 7 }).next() + t.is(calls, 4) + }) + + // NOTE: adapted from encoding-down + test(`${mode}() does not strip nullish range options (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(12) + + const db1 = mockLevel({ + [privateMethod] (options) { + t.is(options.gt, '\x00', 'encoded null') + t.is(options.gte, '\x00', 'encoded null') + t.is(options.lt, '\x00', 'encoded null') + t.is(options.lte, '\x00', 'encoded null') + + return new Ctor(this, options) + } + }, utf8Manifest, { keyEncoding: nullishEncoding, valueEncoding: nullishEncoding }) + + const db2 = mockLevel({ + [privateMethod] (options) { + t.is(hasOwnProperty.call(options, 'gt'), true) + t.is(hasOwnProperty.call(options, 'gte'), true) + t.is(hasOwnProperty.call(options, 'lt'), true) + t.is(hasOwnProperty.call(options, 'lte'), true) + + t.is(options.gt, '\xff', 'encoded undefined') + t.is(options.gte, '\xff', 'encoded undefined') + t.is(options.lt, '\xff', 'encoded undefined') + t.is(options.lte, '\xff', 'encoded undefined') + + return new Ctor(this, options) + } + }, utf8Manifest, { keyEncoding: nullishEncoding, valueEncoding: nullishEncoding }) + + if (!deferred) { + await Promise.all([db1.open(), db2.open()]) + } + + const promise1 = db1[publicMethod]({ + gt: null, + gte: null, + lt: null, + lte: null + }).next() + + const promise2 = db2[publicMethod]({ + gt: undefined, + gte: undefined, + lt: undefined, + lte: undefined + }).next() + + return Promise.all([promise1, promise2]) + }) + + // NOTE: adapted from encoding-down + test(`${mode}() does not add nullish range options (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(4) + + const db = mockLevel({ + [privateMethod] (options) { + t.is(hasOwnProperty.call(options, 'gt'), false) + t.is(hasOwnProperty.call(options, 'gte'), false) + t.is(hasOwnProperty.call(options, 'lt'), false) + t.is(hasOwnProperty.call(options, 'lte'), false) + + return new Ctor(this, options) + } + }) + + if (!deferred) await db.open() + await db[publicMethod]({}).next() + }) + + // NOTE: adapted from encoding-down + test(`${mode}() encodes seek target (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(2) + + const db = mockLevel({ + [privateMethod] (options) { + return new MockIterator(this, options) + } + }, utf8Manifest, { keyEncoding: 'json' }) + + class MockIterator extends Ctor { + _seek (target, options) { + t.is(target, '"a"', 'encoded once') + t.same(options, { keyEncoding: 'utf8' }) + } + } + + if (!deferred) await db.open() + const it = db[publicMethod]() + it.seek('a') + await it.next() + }) + + // NOTE: adapted from encoding-down + test(`${mode}() encodes seek target with custom encoding (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(1) + + const targets = [] + const db = mockLevel({ + [privateMethod] (options) { + return new MockIterator(this, options) + } + }, utf8Manifest) + + class MockIterator extends Ctor { + _seek (target) { + targets.push(target) + } + } + + if (!deferred) await db.open() + + db[publicMethod]().seek('a') + db[publicMethod]({ keyEncoding: 'json' }).seek('a') + db[publicMethod]().seek('b', { keyEncoding: 'json' }) + + await db.open() + t.same(targets, ['a', '"a"', '"b"'], 'encoded targets') + }) + + // NOTE: adapted from encoding-down + test(`${mode}() encodes nullish seek target (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(1) + + const targets = [] + const db = mockLevel({ + [privateMethod] (options) { + return new MockIterator(this, options) + } + }, utf8Manifest, { keyEncoding: { encode: String, decode: identity, format: 'utf8' } }) + + class MockIterator extends Ctor { + _seek (target) { + targets.push(target) + } + } + + if (!deferred) await db.open() + + // Unlike keys, nullish targets should not be rejected; + // assume that the encoding gives these types meaning. + db[publicMethod]().seek(null) + db[publicMethod]().seek(undefined) + + await db.open() + t.same(targets, ['null', 'undefined'], 'encoded') + }) + + test(`${mode}() has default nextv() (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + const sizes = [[1, [0]], [1, [1]], [2, [2]], [3, [3]]] + t.plan(sizes.length * 2) + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + return new MockIterator(this, options) + } + } + + let pos = 0 + class MockIterator extends Ctor { + _next (callback) { + if (mode === 'iterator' || def) { + this.nextTick(callback, null, 'k' + pos, 'v' + (pos++)) + } else if (mode === 'keys') { + this.nextTick(callback, null, 'k' + (pos++)) + } else { + this.nextTick(callback, null, 'v' + (pos++)) + } + } + } + + const db = new MockLevel(utf8Manifest) + if (!deferred) await db.open() + + let expectedPos = 0 + const it = db[publicMethod]() + + for (const [size, args] of sizes) { + const actual = await it.nextv(...args) + const expected = [] + + for (let i = 0; i < size; i++) { + const pos = expectedPos++ + if (mode === 'iterator') expected.push(['k' + pos, 'v' + pos]) + else if (mode === 'keys') expected.push('k' + pos) + else expected.push('v' + pos) + } + + t.is(actual.length, size) + t.same(actual, expected) + } + }) + + test(`${mode}() default nextv() forwards next() error (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(2) + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + return new MockIterator(this, options) + } + } + + class MockIterator extends Ctor { + _next (callback) { + t.pass('called') + this.nextTick(callback, new Error('test')) + } + } + + const db = new MockLevel(utf8Manifest) + if (!deferred) await db.open() + + try { + await db[publicMethod]().nextv(10) + } catch (err) { + t.is(err.message, 'test') + } + }) + + test(`${mode}() has default all() (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(8) + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + return new MockIterator(this, options) + } + } + + let pos = 0 + class MockIterator extends Ctor { + _nextv (size, options, callback) { + t.is(size, 1000) + t.same(options, {}) + + if (pos === 4) { + this.nextTick(callback, null, []) + } else if (mode === 'iterator' || def) { + this.nextTick(callback, null, [[String(pos++), 'a'], [String(pos++), 'b']]) + } else if (mode === 'keys') { + this.nextTick(callback, null, [String(pos++), String(pos++)]) + } else { + pos += 2 + this.nextTick(callback, null, ['a', 'b']) + } + } + + _close (callback) { + t.pass('closed') + this.nextTick(callback) + } + } + + const db = new MockLevel(utf8Manifest) + if (!deferred) await db.open() + + t.same(await db[publicMethod]().all(), [ + ['0', 'a'], + ['1', 'b'], + ['2', 'a'], + ['3', 'b'] + ].map(kv => mode === 'iterator' ? kv : kv[mode === 'keys' ? 0 : 1])) + }) + + test(`${mode}() default all() forwards nextv() error (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(2) + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + return new MockIterator(this, options) + } + } + + class MockIterator extends Ctor { + _nextv (size, options, callback) { + t.pass('called') + this.nextTick(callback, new Error('test')) + } + } + + const db = new MockLevel(utf8Manifest) + if (!deferred) await db.open() + + try { + await db[publicMethod]().all() + } catch (err) { + t.is(err.message, 'test') + } + }) + + test(`${mode}() custom all() (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(3) + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + return new MockIterator(this, options) + } + } + + class MockIterator extends Ctor { + _all (options, callback) { + t.same(options, {}) + + if (mode === 'iterator' || def) { + this.nextTick(callback, null, [['k0', 'v0'], ['k1', 'v1']]) + } else if (mode === 'keys') { + this.nextTick(callback, null, ['k0', 'k1']) + } else { + this.nextTick(callback, null, ['v0', 'v1']) + } + } + + _close (callback) { + t.pass('closed') + this.nextTick(callback) + } + } + + const db = new MockLevel(utf8Manifest) + if (!deferred) await db.open() + + t.same(await db[publicMethod]().all(), [ + ['k0', 'v0'], + ['k1', 'v1'] + ].map(kv => mode === 'iterator' ? kv : kv[mode === 'keys' ? 0 : 1])) + }) + + test(`${mode}() custom all() forwards error and closes (deferred: ${deferred}, default implementation: ${def})`, async function (t) { + t.plan(3) + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + return new MockIterator(this, options) + } + } + + class MockIterator extends Ctor { + _all (options, callback) { + t.pass('_all called') + this.nextTick(callback, new Error('test')) + } + + _close (callback) { + t.pass('closed') + this.nextTick(callback) + } + } + + const db = new MockLevel(utf8Manifest) + if (!deferred) await db.open() + + try { + await db[publicMethod]().all() + } catch (err) { + t.is(err.message, 'test') + } + }) + } +} + +for (const deferred of [false, true]) { + // NOTE: adapted from encoding-down + test(`iterator() skips decoding keys if options.keys is false (deferred: ${deferred})`, async function (t) { + t.plan(3) + + const keyEncoding = { + format: 'utf8', + decode (key) { + t.fail('should not be called') + }, + encode: identity + } + + const db = mockLevel({ + _iterator (options) { + t.is(options.keys, false) + + return mockIterator(this, options, { + _next (callback) { + this.nextTick(callback, null, '', 'value') + } + }) + } + }, utf8Manifest, { keyEncoding }) + + if (!deferred) await db.open() + const [key, value] = await db.iterator({ keys: false }).next() + + t.is(key, undefined, 'normalized key to undefined') + t.is(value, 'value', 'got value') + }) + + // NOTE: adapted from encoding-down + test(`iterator() skips decoding values if options.values is false (deferred: ${deferred})`, async function (t) { + t.plan(3) + + const valueEncoding = { + format: 'utf8', + decode (value) { + t.fail('should not be called') + }, + encode: identity + } + + const db = mockLevel({ + _iterator (options) { + t.is(options.values, false) + + return mockIterator(this, options, { + _next (callback) { + callback(null, 'key', '') + } + }) + } + }, utf8Manifest, { valueEncoding }) + + if (!deferred) await db.open() + const [key, value] = await db.iterator({ values: false }).next() + + t.is(key, 'key', 'got key') + t.is(value, undefined, 'normalized value to undefined') + }) +} + +function spy (fn) { + const wrapped = function (...args) { + wrapped.calls++ + return fn(...args) + } + wrapped.calls = 0 + return wrapped +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/sublevel-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/sublevel-test.js new file mode 100644 index 0000000..b6fcbb2 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/self/sublevel-test.js @@ -0,0 +1,931 @@ +'use strict' + +const test = require('tape') +const { Buffer } = require('buffer') +const { AbstractLevel, AbstractSublevel } = require('../..') +const { AbstractIterator, AbstractKeyIterator, AbstractValueIterator } = require('../..') +const nextTick = AbstractLevel.prototype.nextTick + +class NoopLevel extends AbstractLevel { + constructor (...args) { + super( + { encodings: { utf8: true, buffer: true, view: true } }, + ...args + ) + } +} + +test('sublevel is extensible', function (t) { + t.plan(6) + + class MockLevel extends AbstractLevel { + _sublevel (name, options) { + t.is(name, 'test') + t.same(options, { separator: '!', customOption: 123 }) + + return new MockSublevel(this, name, { + ...options, + manifest: { + encodings: { ignored: true }, + additionalMethods: { test: true }, + events: { foo: true } + } + }) + } + } + + class MockSublevel extends AbstractSublevel { + test () { + this.emit('foo') + } + } + + const db = new MockLevel({ + encodings: { utf8: true }, + additionalMethods: { ignored: true }, + events: { ignored: true } + }) + + const sub = db.sublevel('test', { customOption: 123 }) + + t.is(sub.supports.encodings.ignored, undefined) + t.same(sub.supports.additionalMethods, { test: true }) + t.same(sub.supports.events, { + foo: true, + + // Added by AbstractLevel + opening: true, + open: true, + closing: true, + closed: true, + put: true, + del: true, + batch: true, + clear: true + }) + + sub.on('foo', () => t.pass('emitted')) + sub.test() +}) + +// NOTE: adapted from subleveldown +test('sublevel prefix and options', function (t) { + t.test('empty prefix', function (t) { + const sub = new NoopLevel().sublevel('') + t.is(sub.prefix, '!!') + t.end() + }) + + t.test('prefix without options', function (t) { + const sub = new NoopLevel().sublevel('prefix') + t.is(sub.prefix, '!prefix!') + t.end() + }) + + t.test('prefix and separator option', function (t) { + const sub = new NoopLevel().sublevel('prefix', { separator: '%' }) + t.is(sub.prefix, '%prefix%') + t.end() + }) + + t.test('separator is trimmed from prefix', function (t) { + const sub1 = new NoopLevel().sublevel('!prefix') + t.is(sub1.prefix, '!prefix!') + + const sub2 = new NoopLevel().sublevel('prefix!') + t.is(sub2.prefix, '!prefix!') + + const sub3 = new NoopLevel().sublevel('!!prefix!!') + t.is(sub3.prefix, '!prefix!') + + const sub4 = new NoopLevel().sublevel('@prefix@', { separator: '@' }) + t.is(sub4.prefix, '@prefix@') + + t.end() + }) + + t.test('repeated separator can not result in empty prefix', function (t) { + const sub = new NoopLevel().sublevel('!!!!') + t.is(sub.prefix, '!!') + t.end() + }) + + t.test('invalid sublevel prefix', function (t) { + t.throws(() => new NoopLevel().sublevel('foo\x05'), (err) => err.code === 'LEVEL_INVALID_PREFIX') + t.throws(() => new NoopLevel().sublevel('foo\xff'), (err) => err.code === 'LEVEL_INVALID_PREFIX') + t.throws(() => new NoopLevel().sublevel('foo!', { separator: '@' }), (err) => err.code === 'LEVEL_INVALID_PREFIX') + t.end() + }) + + t.test('legacy sublevel(down) options', function (t) { + t.throws(() => new NoopLevel().sublevel('foo', 'bar'), (err) => err.code === 'LEVEL_LEGACY') + t.throws(() => new NoopLevel().sublevel('foo', { open: () => {} }), (err) => err.code === 'LEVEL_LEGACY') + t.end() + }) + + // See https://github.com/Level/subleveldown/issues/78 + t.test('doubly nested sublevel has correct prefix', async function (t) { + t.plan(1) + + const keys = [] + class MockLevel extends AbstractLevel { + _put (key, value, options, callback) { + keys.push(key) + nextTick(callback) + } + } + + const db = new MockLevel({ encodings: { utf8: true } }) + const sub1 = db.sublevel('1') + const sub2 = sub1.sublevel('2') + const sub3 = sub2.sublevel('3') + + await sub1.put('a', 'value') + await sub2.put('b', 'value') + await sub3.put('c', 'value') + + t.same(keys.sort(), [ + '!1!!2!!3!c', + '!1!!2!b', + '!1!a' + ]) + }) + + t.end() +}) + +test('sublevel.prefixKey()', function (t) { + const db = new AbstractLevel({ encodings: { utf8: true, buffer: true, view: true } }) + const sub = db.sublevel('test') + const textEncoder = new TextEncoder() + + t.same(sub.prefixKey('', 'utf8'), '!test!') + t.same(sub.prefixKey('a', 'utf8'), '!test!a') + + t.same(sub.prefixKey(Buffer.from(''), 'buffer'), Buffer.from('!test!')) + t.same(sub.prefixKey(Buffer.from('a'), 'buffer'), Buffer.from('!test!a')) + + t.same(sub.prefixKey(textEncoder.encode(''), 'view'), textEncoder.encode('!test!')) + t.same(sub.prefixKey(textEncoder.encode('a'), 'view'), textEncoder.encode('!test!a')) + + t.end() +}) + +// NOTE: adapted from subleveldown +test('sublevel manifest and parent db', function (t) { + t.test('sublevel inherits manifest from parent db', function (t) { + const parent = new AbstractLevel({ + encodings: { utf8: true }, + seek: true, + foo: true + }) + const sub = parent.sublevel('') + t.is(sub.supports.foo, true, 'AbstractSublevel inherits from parent') + t.is(sub.supports.seek, true, 'AbstractSublevel inherits from parent') + t.end() + }) + + t.test('sublevel does not support additionalMethods', function (t) { + const parent = new AbstractLevel({ + encodings: { utf8: true }, + additionalMethods: { foo: true } + }) + + // We're expecting that AbstractSublevel removes the additionalMethod + // because it can't automatically prefix any key(-like) arguments + const sub = parent.sublevel('') + t.same(sub.supports.additionalMethods, {}) + t.same(parent.supports.additionalMethods, { foo: true }) + t.is(typeof sub.foo, 'undefined', 'AbstractSublevel does not expose method') + t.end() + }) + + t.test('sublevel.db is set to parent db', function (t) { + const db = new NoopLevel() + const sub = db.sublevel('test') + sub.once('open', function () { + t.ok(sub.db instanceof NoopLevel) + t.end() + }) + }) + + t.end() +}) + +// NOTE: adapted from subleveldown +test('opening & closing sublevel', function (t) { + t.test('error from open() does not bubble up to sublevel', function (t) { + t.plan(5) + + class MockLevel extends AbstractLevel { + _open (opts, cb) { + nextTick(cb, new Error('error from underlying store')) + } + } + + const db = new MockLevel({ encodings: { buffer: true } }) + const sub = db.sublevel('test') + + db.open((err) => { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + t.is(err && err.cause && err.cause.message, 'error from underlying store') + }) + + sub.open((err) => { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN') + t.is(err && err.cause && err.cause.code, 'LEVEL_DATABASE_NOT_OPEN') // from db + t.is(err && err.cause && err.cause.cause, undefined) // but does not have underlying error + }) + }) + + t.test('cannot create a sublevel on a closed db', function (t) { + t.plan(4) + + const db = new NoopLevel() + + db.once('open', function () { + db.close(function (err) { + t.error(err, 'no error') + + db.sublevel('test').open(function (err) { + t.is(err && err.code, 'LEVEL_DATABASE_NOT_OPEN', 'sublevel not opened') + + db.open(function (err) { + t.error(err, 'no error') + + db.sublevel('test').on('open', function () { + t.pass('sublevel opened') + }) + }) + }) + }) + }) + }) + + t.test('can close db and sublevel once opened', function (t) { + t.plan(3) + + const db = new NoopLevel() + + db.open(function (err) { + t.ifError(err, 'no open error') + const sub = db.sublevel('test') + + sub.once('open', function () { + db.close(function (err) { + t.ifError(err, 'no close error') + }) + + sub.close(function (err) { + t.ifError(err, 'no close error') + }) + }) + }) + }) + + t.test('sublevel rejects operations if parent db is closed', function (t) { + t.plan(9) + + const db = new NoopLevel() + + db.open(function (err) { + t.ifError(err, 'no open error') + + const sub = db.sublevel('test') + const it = sub.iterator() + + sub.once('open', function () { + db.close(function (err) { + t.ifError(err, 'no close error') + + sub.put('foo', 'bar', verify) + sub.get('foo', verify) + sub.del('foo', verify) + sub.clear(verify) + sub.batch([{ type: 'del', key: 'foo' }], verify) + + it.next(function (err) { + t.is(err.code, 'LEVEL_ITERATOR_NOT_OPEN') + it.close(t.ifError.bind(t)) + }) + + function verify (err) { + t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN') + } + }) + }) + }) + }) + + t.test('cannot close db while sublevel is opening', function (t) { + t.plan(5) + + const db = new NoopLevel() + + db.open(function (err) { + t.ifError(err, 'no open error') + const sub = db.sublevel('test') + + sub.open((err) => { + t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN') + }) + + db.close(function (err) { + t.ifError(err, 'no close error') + t.is(sub.status, 'closed') + t.is(db.status, 'closed') + }) + }) + }) + + t.test('cannot create sublevel while db is closing', function (t) { + t.plan(5) + + const db = new NoopLevel() + + db.open(function (err) { + t.ifError(err, 'no open error') + + db.close(function (err) { + t.ifError(err, 'no close error') + t.is(db.status, 'closed') + }) + + const sub = db.sublevel('test') + + sub.open((err) => { + t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN') + t.is(sub.status, 'closed') + }) + }) + }) + + t.test('can wrap a sublevel and reopen the wrapped sublevel', function (t) { + const db = new NoopLevel() + const sub1 = db.sublevel('test1') + const sub2 = sub1.sublevel('test2') + + sub2.once('open', function () { + verify() + + sub2.close(function (err) { + t.ifError(err, 'no close error') + + // Prefixes should be the same after closing & reopening + // See https://github.com/Level/subleveldown/issues/78 + sub2.open(function (err) { + t.ifError(err, 'no open error') + verify() + t.end() + }) + }) + }) + + function verify () { + t.is(sub1.prefix, '!test1!', 'sub1 prefix ok') + t.ok(sub1.db instanceof NoopLevel) + t.is(sub2.prefix, '!test1!!test2!', 'sub2 prefix ok') + t.ok(sub2.db instanceof NoopLevel) + } + }) + + // Also test default fallback implementations of keys() and values() + for (const [mode, def] of [['iterator', false], ['keys', false], ['values', false], ['keys', true], ['values', true]]) { + const Ctor = mode === 'iterator' || def ? AbstractIterator : mode === 'keys' ? AbstractKeyIterator : AbstractValueIterator + const privateMethod = def ? '_iterator' : '_' + mode + const publicMethod = mode + + t.test(`error from sublevel.${mode}() bubbles up (default implementation: ${def})`, function (t) { + t.plan(2) + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + return new MockIterator(this, options) + } + } + + class MockIterator extends Ctor { + _next (callback) { + this.nextTick(callback, new Error('next() error from parent database')) + } + } + + const db = new MockLevel({ encodings: { buffer: true } }) + const sub = db.sublevel('test') + const it = sub[publicMethod]() + + it.next(function (err) { + t.is(err.message, 'next() error from parent database') + + it.close(function () { + t.pass('closed') + }) + }) + }) + } + + t.end() +}) + +test('sublevel operations are prefixed', function (t) { + t.test('sublevel.getMany() is prefixed', async function (t) { + t.plan(2) + + class MockLevel extends AbstractLevel { + _getMany (keys, options, callback) { + t.same(keys, ['!test!a', '!test!b']) + t.same(options, { keyEncoding: 'utf8', valueEncoding: 'utf8' }) + nextTick(callback, null, ['1', '2']) + } + } + + const db = new MockLevel({ encodings: { utf8: true } }) + const sub = db.sublevel('test') + + await sub.open() + await sub.getMany(['a', 'b']) + }) + + // Also test default fallback implementations of keys() and values() + for (const [mode, def] of [['iterator', false], ['keys', false], ['values', false], ['keys', true], ['values', true]]) { + const Ctor = mode === 'iterator' || def ? AbstractIterator : mode === 'keys' ? AbstractKeyIterator : AbstractValueIterator + const privateMethod = def ? '_iterator' : '_' + mode + const publicMethod = mode + + for (const deferred of [false, true]) { + t.test(`sublevel ${mode}.seek() target is prefixed (default implementation: ${def}, deferred: ${deferred})`, async function (t) { + t.plan(2) + + class MockIterator extends Ctor { + _seek (target, options) { + t.is(target, '!sub!123') + t.is(options.keyEncoding, 'utf8') + } + } + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + return new MockIterator(this, options) + } + } + + const db = new MockLevel({ encodings: { utf8: true } }) + const sub = db.sublevel('sub', { keyEncoding: 'json' }) + + if (!deferred) await sub.open() + + const it = sub[publicMethod]() + it.seek(123) + + if (deferred) await sub.open() + }) + } + } + + t.test('sublevel.clear() is prefixed', async function (t) { + t.plan(4) + + const calls = [] + class MockLevel extends AbstractLevel { + _clear (options, callback) { + calls.push(options) + nextTick(callback) + } + } + + const db = new MockLevel({ encodings: { utf8: true } }) + const sub = db.sublevel('sub') + + const test = async (options, expected) => { + await sub.clear(options) + t.same(calls.shift(), expected) + } + + await sub.open() + + await test(undefined, { + gte: '!sub!', + lte: '!sub"', + keyEncoding: 'utf8', + reverse: false, + limit: -1 + }) + + await test({ gt: 'a' }, { + gt: '!sub!a', + lte: '!sub"', + keyEncoding: 'utf8', + reverse: false, + limit: -1 + }) + + await test({ gte: 'a', lt: 'x' }, { + gte: '!sub!a', + lt: '!sub!x', + keyEncoding: 'utf8', + reverse: false, + limit: -1 + }) + + await test({ lte: 'x' }, { + gte: '!sub!', + lte: '!sub!x', + keyEncoding: 'utf8', + reverse: false, + limit: -1 + }) + }) + + t.end() +}) + +test('sublevel encodings', function (t) { + // NOTE: adapted from subleveldown + t.test('different sublevels can have different encodings', function (t) { + t.plan(10) + + const puts = [] + const gets = [] + + class MockLevel extends AbstractLevel { + _put (key, value, { keyEncoding, valueEncoding }, callback) { + puts.push({ key, value, keyEncoding, valueEncoding }) + nextTick(callback) + } + + _get (key, { keyEncoding, valueEncoding }, callback) { + gets.push({ key, keyEncoding, valueEncoding }) + nextTick(callback, null, puts.shift().value) + } + } + + const db = new MockLevel({ encodings: { buffer: true, utf8: true } }) + const sub1 = db.sublevel('test1', { valueEncoding: 'json' }) + const sub2 = db.sublevel('test2', { keyEncoding: 'buffer', valueEncoding: 'buffer' }) + + sub1.put('foo', { some: 'json' }, function (err) { + t.error(err, 'no error') + + t.same(puts, [{ + key: '!test1!foo', + value: '{"some":"json"}', + keyEncoding: 'utf8', + valueEncoding: 'utf8' + }]) + + sub1.get('foo', function (err, value) { + t.error(err, 'no error') + t.same(value, { some: 'json' }) + t.same(gets.shift(), { + key: '!test1!foo', + keyEncoding: 'utf8', + valueEncoding: 'utf8' + }) + + sub2.put(Buffer.from([1, 2]), Buffer.from([3]), function (err) { + t.error(err, 'no error') + + t.same(puts, [{ + key: Buffer.from('!test2!\x01\x02'), + value: Buffer.from([3]), + keyEncoding: 'buffer', + valueEncoding: 'buffer' + }]) + + sub2.get(Buffer.from([1, 2]), function (err, value) { + t.error(err, 'no error') + t.same(value, Buffer.from([3])) + t.same(gets.shift(), { + key: Buffer.from('!test2!\x01\x02'), + keyEncoding: 'buffer', + valueEncoding: 'buffer' + }) + }) + }) + }) + }) + }) + + t.test('sublevel indirectly supports transcoded encoding', function (t) { + t.plan(5) + + class MockLevel extends AbstractLevel { + _put (key, value, { keyEncoding, valueEncoding }, callback) { + t.same({ key, value, keyEncoding, valueEncoding }, { + key: Buffer.from('!test!foo'), + value: Buffer.from('{"some":"json"}'), + keyEncoding: 'buffer', + valueEncoding: 'buffer' + }) + nextTick(callback) + } + + _get (key, { keyEncoding, valueEncoding }, callback) { + t.same({ key, keyEncoding, valueEncoding }, { + key: Buffer.from('!test!foo'), + keyEncoding: 'buffer', + valueEncoding: 'buffer' + }) + nextTick(callback, null, Buffer.from('{"some":"json"}')) + } + } + + const db = new MockLevel({ encodings: { buffer: true } }) + const sub = db.sublevel('test', { valueEncoding: 'json' }) + + sub.put('foo', { some: 'json' }, function (err) { + t.error(err, 'no error') + + sub.get('foo', function (err, value) { + t.error(err, 'no error') + t.same(value, { some: 'json' }) + }) + }) + }) + + t.test('concatenating sublevel Buffer keys', function (t) { + t.plan(10) + + const key = Buffer.from('00ff', 'hex') + const prefixedKey = Buffer.concat([Buffer.from('!test!'), key]) + + class MockLevel extends AbstractLevel { + _put (key, value, options, callback) { + t.is(options.keyEncoding, 'buffer') + t.is(options.valueEncoding, 'buffer') + t.same(key, prefixedKey) + t.same(value, Buffer.from('bar')) + nextTick(callback) + } + + _get (key, options, callback) { + t.is(options.keyEncoding, 'buffer') + t.is(options.valueEncoding, 'buffer') + t.same(key, prefixedKey) + nextTick(callback, null, Buffer.from('bar')) + } + } + + const db = new MockLevel({ encodings: { buffer: true } }) + const sub = db.sublevel('test', { keyEncoding: 'buffer' }) + + sub.put(key, 'bar', function (err) { + t.ifError(err) + sub.get(key, function (err, value) { + t.ifError(err) + t.is(value, 'bar') + }) + }) + }) + + t.test('concatenating sublevel Uint8Array keys', function (t) { + t.plan(10) + + const key = new Uint8Array([0, 255]) + const textEncoder = new TextEncoder() + const prefix = textEncoder.encode('!test!') + const prefixedKey = new Uint8Array(prefix.byteLength + key.byteLength) + + prefixedKey.set(prefix, 0) + prefixedKey.set(key, prefix.byteLength) + + class MockLevel extends AbstractLevel { + _put (key, value, options, callback) { + t.is(options.keyEncoding, 'view') + t.is(options.valueEncoding, 'view') + t.same(key, prefixedKey) + t.same(value, textEncoder.encode('bar')) + nextTick(callback) + } + + _get (key, options, callback) { + t.is(options.keyEncoding, 'view') + t.is(options.valueEncoding, 'view') + t.same(key, prefixedKey) + nextTick(callback, null, textEncoder.encode('bar')) + } + } + + const db = new MockLevel({ encodings: { view: true } }) + const sub = db.sublevel('test', { keyEncoding: 'view' }) + + sub.put(key, 'bar', function (err) { + t.ifError(err) + sub.get(key, function (err, value) { + t.ifError(err) + t.is(value, 'bar') + }) + }) + }) + + // Also test default fallback implementations of keys() and values() + for (const [mode, def] of [['iterator', false], ['keys', false], ['values', false], ['keys', true], ['values', true]]) { + const Ctor = mode === 'iterator' || def ? AbstractIterator : mode === 'keys' ? AbstractKeyIterator : AbstractValueIterator + const privateMethod = def ? '_iterator' : '_' + mode + const publicMethod = mode + + t.test(`unfixing sublevel.${mode}() Buffer keys (default implementation: ${def})`, function (t) { + t.plan(4) + + const testKey = Buffer.from('00ff', 'hex') + const prefixedKey = Buffer.concat([Buffer.from('!test!'), testKey]) + + class MockIterator extends Ctor { + _next (callback) { + if (mode === 'iterator' || def) { + this.nextTick(callback, null, prefixedKey, 'bar') + } else if (mode === 'keys') { + this.nextTick(callback, null, prefixedKey) + } else { + this.nextTick(callback, null, 'bar') + } + } + } + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + t.is(options.keyEncoding, 'buffer') + t.is(options.valueEncoding, 'utf8') + return new MockIterator(this, options) + } + } + + const db = new MockLevel({ encodings: { buffer: true, view: true, utf8: true } }) + const sub = db.sublevel('test', { keyEncoding: 'buffer' }) + + sub[publicMethod]().next(function (err, keyOrValue) { + t.ifError(err) + t.same(keyOrValue, mode === 'values' ? 'bar' : testKey) + }) + }) + + t.test(`unfixing sublevel.${mode}() Uint8Array keys (default implementation: ${def})`, function (t) { + t.plan(4) + + const testKey = new Uint8Array([0, 255]) + const textEncoder = new TextEncoder() + const prefix = textEncoder.encode('!test!') + const prefixedKey = new Uint8Array(prefix.byteLength + testKey.byteLength) + + prefixedKey.set(prefix, 0) + prefixedKey.set(testKey, prefix.byteLength) + + class MockIterator extends Ctor { + _next (callback) { + if (mode === 'iterator' || def) { + this.nextTick(callback, null, prefixedKey, 'bar') + } else if (mode === 'keys') { + this.nextTick(callback, null, prefixedKey) + } else { + this.nextTick(callback, null, 'bar') + } + } + } + + class MockLevel extends AbstractLevel { + [privateMethod] (options) { + t.is(options.keyEncoding, 'view') + t.is(options.valueEncoding, 'utf8') + return new MockIterator(this, options) + } + } + + const db = new MockLevel({ encodings: { buffer: true, view: true, utf8: true } }) + const sub = db.sublevel('test', { keyEncoding: 'view' }) + + sub[publicMethod]().next(function (err, keyOrValue) { + t.ifError(err) + t.same(keyOrValue, mode === 'values' ? 'bar' : testKey) + }) + }) + } + + t.end() +}) + +for (const chained of [false, true]) { + for (const deferred of [false, true]) { + test(`batch() with sublevel per operation (chained: ${chained}, deferred: ${deferred})`, async function (t) { + t.plan(6) + + class MockLevel extends AbstractLevel { + _batch (operations, options, callback) { + t.same(operations, [ + { + type: 'put', + sublevel: null, + key: '!1!a', + value: '{"foo":123}', + keyEncoding: 'utf8', + valueEncoding: 'utf8' + }, + { + type: 'put', + sublevel: null, + key: '!2!a-y', + value: '[object Object]', + keyEncoding: 'utf8', + valueEncoding: 'utf8' + }, + { + type: 'put', + sublevel: null, + key: '!1!b', + value: '[object Object]', + keyEncoding: 'utf8', + valueEncoding: 'utf8' + }, + { + type: 'put', + sublevel: null, + key: '!2!b', + value: 'b', + keyEncoding: 'utf8', + valueEncoding: 'utf8' + }, + { + type: 'del', + sublevel: null, + key: '!2!c1', + keyEncoding: 'utf8' + }, + { + type: 'del', + sublevel: null, + key: '!2!c2-y', + keyEncoding: 'utf8' + }, + { + type: 'del', + key: 'd-x', + keyEncoding: 'utf8' + } + ]) + t.same(options, {}) + nextTick(callback) + } + } + + const db = new MockLevel({ encodings: { utf8: true } }, { + keyEncoding: { + encode: (key) => key + '-x', + decode: (key) => key.slice(0, -2), + name: 'x', + format: 'utf8' + } + }) + + const sub1 = db.sublevel('1', { valueEncoding: 'json' }) + const sub2 = db.sublevel('2', { + keyEncoding: { + encode: (key) => key + '-y', + decode: (key) => key.slice(0, -2), + name: 'y', + format: 'utf8' + } + }) + + if (!deferred) await sub1.open() + + t.is(sub1.keyEncoding().name, 'utf8') + t.is(sub1.valueEncoding().name, 'json') + t.is(sub2.keyEncoding().name, 'y') + t.is(sub2.valueEncoding().name, 'utf8') + + if (chained) { + await db.batch() + // keyEncoding: utf8 (sublevel), valueEncoding: json (sublevel) + .put('a', { foo: 123 }, { sublevel: sub1 }) + + // keyEncoding: y (sublevel), valueEncoding: utf8 (sublevel) + .put('a', { foo: 123 }, { sublevel: sub2 }) + + // keyEncoding: utf8 (sublevel), valueEncoding: utf8 (operation) + .put('b', { foo: 123 }, { sublevel: sub1, valueEncoding: 'utf8' }) + + // keyEncoding: utf8 (operation), valueEncoding: utf8 (sublevel) + .put('b', 'b', { sublevel: sub2, keyEncoding: 'utf8' }) + + // keyEncoding: utf8 (operation) + .del('c1', { sublevel: sub2, keyEncoding: 'utf8' }) + + // keyEncoding: y (sublevel) + .del('c2', { sublevel: sub2 }) + + // keyEncoding: x (db). Should not affect sublevels. + .del('d') + .write() + } else { + await db.batch([ + { type: 'put', sublevel: sub1, key: 'a', value: { foo: 123 } }, + { type: 'put', sublevel: sub2, key: 'a', value: { foo: 123 } }, + { type: 'put', sublevel: sub1, key: 'b', value: { foo: 123 }, valueEncoding: 'utf8' }, + { type: 'put', sublevel: sub2, key: 'b', value: 'b', keyEncoding: 'utf8' }, + { type: 'del', key: 'c1', sublevel: sub2, keyEncoding: 'utf8' }, + { type: 'del', key: 'c2', sublevel: sub2 }, + { type: 'del', key: 'd' } + ]) + } + }) + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/sublevel-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/sublevel-test.js new file mode 100644 index 0000000..38c8dc5 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/sublevel-test.js @@ -0,0 +1,108 @@ +'use strict' + +const { Buffer } = require('buffer') + +exports.all = function (test, testCommon) { + for (const deferred of [false, true]) { + // NOTE: adapted from subleveldown + test(`sublevel.clear() (deferred: ${deferred})`, async function (t) { + const db = testCommon.factory() + const sub1 = db.sublevel('1') + const sub2 = db.sublevel('2') + + if (!deferred) await sub1.open() + if (!deferred) await sub2.open() + + await populate([sub1, sub2], ['a', 'b']) + await verify(['!1!a', '!1!b', '!2!a', '!2!b']) + + await clear([sub1], {}) + await verify(['!2!a', '!2!b']) + + await populate([sub1], ['a', 'b']) + await clear([sub2], { lt: 'b' }) + await verify(['!1!a', '!1!b', '!2!b']) + await db.close() + + async function populate (subs, items) { + return Promise.all(subs.map(sub => { + return sub.batch(items.map(function (item) { + return { type: 'put', key: item, value: item } + })) + })) + } + + async function clear (subs, opts) { + return Promise.all(subs.map(sub => { + return sub.clear(opts) + })) + } + + async function verify (expected) { + const keys = await db.keys().all() + t.same(keys, expected) + } + }) + } + + for (const deferred of [false, true]) { + for (const keyEncoding of ['buffer', 'view']) { + if (!testCommon.supports.encodings[keyEncoding]) return + + // NOTE: adapted from subleveldown. See https://github.com/Level/subleveldown/issues/87 + test(`iterate sublevel keys with bytes above 196 (${keyEncoding}, deferred: ${deferred})`, async function (t) { + const db = testCommon.factory() + const sub1 = db.sublevel('a', { keyEncoding }) + const sub2 = db.sublevel('b', { keyEncoding }) + const length = (db) => db.keys().all().then(x => x.length) + + if (!deferred) await sub1.open() + if (!deferred) await sub2.open() + + const batch1 = sub1.batch() + const batch2 = sub2.batch() + const keys = [] + + for (let i = 0; i < 256; i++) { + const key = keyEncoding === 'buffer' ? Buffer.from([i]) : new Uint8Array([i]) + keys.push(key) + batch1.put(key, 'aa') + batch2.put(key, 'bb') + } + + await Promise.all([batch1.write(), batch2.write()]) + + const entries1 = await sub1.iterator().all() + const entries2 = await sub2.iterator().all() + + t.is(entries1.length, 256, 'sub1 yielded all entries') + t.is(entries2.length, 256, 'sub2 yielded all entries') + t.ok(entries1.every(x => x[1] === 'aa')) + t.ok(entries2.every(x => x[1] === 'bb')) + + const many1 = await sub1.getMany(keys) + const many2 = await sub2.getMany(keys) + + t.is(many1.length, 256, 'sub1 yielded all values') + t.is(many2.length, 256, 'sub2 yielded all values') + t.ok(many1.every(x => x === 'aa')) + t.ok(many2.every(x => x === 'bb')) + + const singles1 = await Promise.all(keys.map(k => sub1.get(k))) + const singles2 = await Promise.all(keys.map(k => sub2.get(k))) + + t.is(singles1.length, 256, 'sub1 yielded all values') + t.is(singles2.length, 256, 'sub2 yielded all values') + t.ok(singles1.every(x => x === 'aa')) + t.ok(singles2.every(x => x === 'bb')) + + await sub1.clear() + + t.same(await length(sub1), 0, 'cleared sub1') + t.same(await length(sub2), 256, 'did not clear sub2') + + await db.close() + }) + } + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/util.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/util.js new file mode 100644 index 0000000..d2ccbb7 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/test/util.js @@ -0,0 +1,280 @@ +'use strict' + +const ModuleError = require('module-error') +const { AbstractLevel, AbstractChainedBatch } = require('..') +const { AbstractIterator, AbstractKeyIterator, AbstractValueIterator } = require('..') + +const spies = [] + +exports.verifyNotFoundError = function (err) { + return err.code === 'LEVEL_NOT_FOUND' && err.notFound === true && err.status === 404 +} + +exports.illegalKeys = [ + { name: 'null key', key: null }, + { name: 'undefined key', key: undefined } +] + +exports.illegalValues = [ + { name: 'null key', value: null }, + { name: 'undefined value', value: undefined } +] + +/** + * Wrap a callback to check that it's called asynchronously. Must be + * combined with a `ctx()`, `with()` or `end()` call. + * + * @param {function} cb Callback to check. + * @param {string} name Optional callback name to use in assertion messages. + * @returns {function} Wrapped callback. + */ +exports.assertAsync = function (cb, name) { + const spy = { + called: false, + name: name || cb.name || 'anonymous' + } + + spies.push(spy) + + return function (...args) { + spy.called = true + return cb.apply(this, args) + } +} + +/** + * Verify that callbacks wrapped with `assertAsync()` were not yet called. + * @param {import('tape').Test} t Tape test object. + */ +exports.assertAsync.end = function (t) { + for (const { called, name } of spies.splice(0, spies.length)) { + t.is(called, false, `callback (${name}) is asynchronous`) + } +} + +/** + * Wrap a test function to verify `assertAsync()` spies at the end. + * @param {import('tape').TestCase} test Test function to be passed to `tape()`. + * @returns {import('tape').TestCase} Wrapped test function. + */ +exports.assertAsync.ctx = function (test) { + return function (...args) { + const ret = test.call(this, ...args) + exports.assertAsync.end(args[0]) + return ret + } +} + +/** + * Wrap an arbitrary callback to verify `assertAsync()` spies at the end. + * @param {import('tape').Test} t Tape test object. + * @param {function} cb Callback to wrap. + * @returns {function} Wrapped callback. + */ +exports.assertAsync.with = function (t, cb) { + return function (...args) { + const ret = cb.call(this, ...args) + exports.assertAsync.end(t) + return ret + } +} + +exports.mockLevel = function (methods, ...args) { + class TestLevel extends AbstractLevel {} + for (const k in methods) TestLevel.prototype[k] = methods[k] + if (!args.length) args = [{ encodings: { utf8: true } }] + return new TestLevel(...args) +} + +exports.mockIterator = function (db, options, methods, ...args) { + class TestIterator extends AbstractIterator {} + for (const k in methods) TestIterator.prototype[k] = methods[k] + return new TestIterator(db, options, ...args) +} + +exports.mockChainedBatch = function (db, methods, ...args) { + class TestBatch extends AbstractChainedBatch {} + for (const k in methods) TestBatch.prototype[k] = methods[k] + return new TestBatch(db, ...args) +} + +// Mock encoding where null and undefined are significant types +exports.nullishEncoding = { + name: 'nullish', + format: 'utf8', + encode (v) { + return v === null ? '\x00' : v === undefined ? '\xff' : String(v) + }, + decode (v) { + return v === '\x00' ? null : v === '\xff' ? undefined : v + } +} + +const kEntries = Symbol('entries') +const kPosition = Symbol('position') +const kOptions = Symbol('options') + +/** + * A minimal and non-optimized implementation for use in tests. Only supports utf8. + * Don't use this as a reference implementation. + */ +class MinimalLevel extends AbstractLevel { + constructor (options) { + super({ encodings: { utf8: true }, seek: true }, options) + this[kEntries] = new Map() + } + + _put (key, value, options, callback) { + this[kEntries].set(key, value) + this.nextTick(callback) + } + + _get (key, options, callback) { + const value = this[kEntries].get(key) + + if (value === undefined) { + return this.nextTick(callback, new ModuleError(`Key ${key} was not found`, { + code: 'LEVEL_NOT_FOUND' + })) + } + + this.nextTick(callback, null, value) + } + + _getMany (keys, options, callback) { + const values = keys.map(k => this[kEntries].get(k)) + this.nextTick(callback, null, values) + } + + _del (key, options, callback) { + this[kEntries].delete(key) + this.nextTick(callback) + } + + _clear (options, callback) { + for (const [k] of sliceEntries(this[kEntries], options, true)) { + this[kEntries].delete(k) + } + + this.nextTick(callback) + } + + _batch (operations, options, callback) { + const entries = new Map(this[kEntries]) + + for (const op of operations) { + if (op.type === 'put') entries.set(op.key, op.value) + else entries.delete(op.key) + } + + this[kEntries] = entries + this.nextTick(callback) + } + + _iterator (options) { + return new MinimalIterator(this, options) + } + + _keys (options) { + return new MinimalKeyIterator(this, options) + } + + _values (options) { + return new MinimalValueIterator(this, options) + } +} + +class MinimalIterator extends AbstractIterator { + constructor (db, options) { + super(db, options) + this[kEntries] = sliceEntries(db[kEntries], options, false) + this[kOptions] = options + this[kPosition] = 0 + } +} + +class MinimalKeyIterator extends AbstractKeyIterator { + constructor (db, options) { + super(db, options) + this[kEntries] = sliceEntries(db[kEntries], options, false) + this[kOptions] = options + this[kPosition] = 0 + } +} + +class MinimalValueIterator extends AbstractValueIterator { + constructor (db, options) { + super(db, options) + this[kEntries] = sliceEntries(db[kEntries], options, false) + this[kOptions] = options + this[kPosition] = 0 + } +} + +for (const Ctor of [MinimalIterator, MinimalKeyIterator, MinimalValueIterator]) { + const mapEntry = Ctor === MinimalIterator ? e => e : Ctor === MinimalKeyIterator ? e => e[0] : e => e[1] + + Ctor.prototype._next = function (callback) { + const entry = this[kEntries][this[kPosition]++] + if (entry === undefined) return this.nextTick(callback) + if (Ctor === MinimalIterator) this.nextTick(callback, null, entry[0], entry[1]) + else this.nextTick(callback, null, mapEntry(entry)) + } + + Ctor.prototype._nextv = function (size, options, callback) { + const entries = this[kEntries].slice(this[kPosition], this[kPosition] + size) + this[kPosition] += entries.length + this.nextTick(callback, null, entries.map(mapEntry)) + } + + Ctor.prototype._all = function (options, callback) { + const end = this.limit - this.count + this[kPosition] + const entries = this[kEntries].slice(this[kPosition], end) + this[kPosition] = this[kEntries].length + this.nextTick(callback, null, entries.map(mapEntry)) + } + + Ctor.prototype._seek = function (target, options) { + this[kPosition] = this[kEntries].length + + if (!outOfRange(target, this[kOptions])) { + // Don't care about performance here + for (let i = 0; i < this[kPosition]; i++) { + const key = this[kEntries][i][0] + + if (this[kOptions].reverse ? key <= target : key >= target) { + this[kPosition] = i + } + } + } + } +} + +const outOfRange = function (target, options) { + if ('gte' in options) { + if (target < options.gte) return true + } else if ('gt' in options) { + if (target <= options.gt) return true + } + + if ('lte' in options) { + if (target > options.lte) return true + } else if ('lt' in options) { + if (target >= options.lt) return true + } + + return false +} + +const sliceEntries = function (entries, options, applyLimit) { + entries = Array.from(entries) + .filter((e) => !outOfRange(e[0], options)) + .sort((a, b) => a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0) + + if (options.reverse) entries.reverse() + if (applyLimit && options.limit !== -1) entries = entries.slice(0, options.limit) + + return entries +} + +exports.MinimalLevel = MinimalLevel diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/abstract-chained-batch.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/abstract-chained-batch.d.ts new file mode 100644 index 0000000..0d5e305 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/abstract-chained-batch.d.ts @@ -0,0 +1,123 @@ +import * as Transcoder from 'level-transcoder' +import { AbstractSublevel } from './abstract-sublevel' +import { NodeCallback } from './interfaces' + +export class AbstractChainedBatch { + constructor (db: TDatabase) + + /** + * A reference to the database that created this chained batch. + */ + db: TDatabase + + /** + * The number of queued operations on the current batch. + */ + get length (): number + + /** + * Queue a _put_ operation on this batch, not committed until {@link write} is + * called. + */ + put (key: KDefault, value: VDefault): this + + put ( + key: K, + value: V, + options: AbstractChainedBatchPutOptions + ): this + + /** + * Queue a _del_ operation on this batch, not committed until {@link write} is + * called. + */ + del (key: KDefault): this + del (key: K, options: AbstractChainedBatchDelOptions): this + + /** + * Clear all queued operations on this batch. + */ + clear (): this + + /** + * Commit the queued operations for this batch. All operations will be written + * atomically, that is, they will either all succeed or fail with no partial + * commits. + */ + write (): Promise + write (options: AbstractChainedBatchWriteOptions): Promise + write (callback: NodeCallback): void + write (options: AbstractChainedBatchWriteOptions, callback: NodeCallback): void + + /** + * Free up underlying resources. This should be done even if the chained batch has + * zero queued operations. Automatically called by {@link write} so normally not + * necessary to call, unless the intent is to discard a chained batch without + * committing it. + */ + close (): Promise + close (callback: NodeCallback): void +} + +/** + * Options for the {@link AbstractChainedBatch.put} method. + */ +export interface AbstractChainedBatchPutOptions { + /** + * Custom key encoding for this _put_ operation, used to encode the `key`. + */ + keyEncoding?: string | Transcoder.PartialEncoder | undefined + + /** + * Custom value encoding for this _put_ operation, used to encode the `value`. + */ + valueEncoding?: string | Transcoder.PartialEncoder | undefined + + /** + * Act as though the _put_ operation is performed on the given sublevel, to similar + * effect as: + * + * ```js + * await sublevel.batch().put(key, value).write() + * ``` + * + * This allows atomically committing data to multiple sublevels. The `key` will be + * prefixed with the `prefix` of the sublevel, and the `key` and `value` will be + * encoded by the sublevel (using the default encodings of the sublevel unless + * {@link keyEncoding} and / or {@link valueEncoding} are provided). + */ + sublevel?: AbstractSublevel | undefined +} + +/** + * Options for the {@link AbstractChainedBatch.del} method. + */ +export interface AbstractChainedBatchDelOptions { + /** + * Custom key encoding for this _del_ operation, used to encode the `key`. + */ + keyEncoding?: string | Transcoder.PartialEncoder | undefined + + /** + * Act as though the _del_ operation is performed on the given sublevel, to similar + * effect as: + * + * ```js + * await sublevel.batch().del(key).write() + * ``` + * + * This allows atomically committing data to multiple sublevels. The `key` will be + * prefixed with the `prefix` of the sublevel, and the `key` will be encoded by the + * sublevel (using the default key encoding of the sublevel unless {@link keyEncoding} + * is provided). + */ + sublevel?: AbstractSublevel | undefined +} + +/** + * Options for the {@link AbstractChainedBatch.write} method. + */ +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AbstractChainedBatchWriteOptions { + // There are no abstract options but implementations may add theirs. +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/abstract-iterator.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/abstract-iterator.d.ts new file mode 100644 index 0000000..7047da6 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/abstract-iterator.d.ts @@ -0,0 +1,252 @@ +import * as Transcoder from 'level-transcoder' +import { RangeOptions, NodeCallback } from './interfaces' + +export interface AbstractIteratorOptions extends RangeOptions { + /** + * Whether to return the key of each entry. Defaults to `true`. If set to `false`, + * the iterator will yield keys that are `undefined`. + */ + keys?: boolean | undefined + + /** + * Whether to return the value of each entry. Defaults to `true`. If set to + * `false`, the iterator will yield values that are `undefined`. + */ + values?: boolean | undefined + + /** + * Custom key encoding for this iterator, used to encode range options, to encode + * {@link AbstractIterator.seek} targets and to decode keys. + */ + keyEncoding?: string | Transcoder.PartialEncoding | undefined + + /** + * Custom value encoding for this iterator, used to decode values. + */ + valueEncoding?: string | Transcoder.PartialDecoder | undefined +} + +export interface AbstractKeyIteratorOptions extends RangeOptions { + /** + * Custom key encoding for this iterator, used to encode range options, to encode + * {@link AbstractKeyIterator.seek} targets and to decode keys. + */ + keyEncoding?: string | Transcoder.PartialEncoding | undefined +} + +export interface AbstractValueIteratorOptions extends RangeOptions { + /** + * Custom key encoding for this iterator, used to encode range options and + * {@link AbstractValueIterator.seek} targets. + */ + keyEncoding?: string | Transcoder.PartialEncoding | undefined + + /** + * Custom value encoding for this iterator, used to decode values. + */ + valueEncoding?: string | Transcoder.PartialDecoder | undefined +} + +/** + * @template TDatabase Type of the database that created this iterator. + * @template T Type of items yielded. Items can be entries, keys or values. + */ +declare class CommonIterator { + /** + * A reference to the database that created this iterator. + */ + db: TDatabase + + /** + * Read-only getter that indicates how many items have been yielded so far (by any + * method) excluding calls that errored or yielded `undefined`. + */ + get count (): number + + /** + * Read-only getter that reflects the `limit` that was set in options. Greater than or + * equal to zero. Equals {@link Infinity} if no limit. + */ + get limit (): number + + [Symbol.asyncIterator] (): AsyncGenerator + + /** + * Free up underlying resources. Not necessary to call if [`for await...of`][1] or + * `all()` is used. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of + */ + close (): Promise + close (callback: NodeCallback): void +} + +export class AbstractIterator extends CommonIterator { + constructor (db: TDatabase, options: AbstractIteratorOptions) + + /** + * Advance to the next entry and yield that entry. When possible, prefer to use + * [`for await...of`][1] instead. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of + */ + next (): Promise<[K, V] | undefined> + next (callback: NextCallback): void + + /** + * Advance repeatedly and get at most {@link size} amount of entries in a single call. + * Can be faster than repeated {@link next()} calls. The natural end of the iterator + * will be signaled by yielding an empty array. + * + * @param size Get at most this many entries. Has a soft minimum of 1. + * @param options Options (none at the moment, reserved for future use). + * @param callback Error-first callback. If none is provided, a promise is returned. + */ + nextv (size: number, options: {}, callback: NodeCallback>): void + nextv (size: number, callback: NodeCallback>): void + nextv (size: number, options: {}): Promise> + nextv (size: number): Promise> + + /** + * Advance repeatedly and get all (remaining) entries as an array, automatically + * closing the iterator. Assumes that those entries fit in memory. If that's not the + * case, instead use {@link next()}, {@link nextv()} or [`for await...of`][1]. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of + * + * @param options Options (none at the moment, reserved for future use). + * @param callback Error-first callback. If none is provided, a promise is returned. + */ + all (options: {}, callback: NodeCallback>): void + all (callback: NodeCallback>): void + all (options: {}): Promise> + all (): Promise> + + /** + * Seek to the key closest to {@link target}. Subsequent calls to {@link next()}, + * {@link nextv()} or {@link all()} (including implicit calls in a `for await...of` + * loop) will yield entries with keys equal to or larger than {@link target}, or equal + * to or smaller than {@link target} if the {@link AbstractIteratorOptions.reverse} + * option was true. + */ + seek (target: K): void + seek (target: TTarget, options: AbstractSeekOptions): void +} + +export class AbstractKeyIterator extends CommonIterator { + constructor (db: TDatabase, options: AbstractKeyIteratorOptions) + + /** + * Advance to the next key and yield that key. When possible, prefer to use + * [`for await...of`][1] instead. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of + */ + next (): Promise + next (callback: NodeCallback): void + + /** + * Advance repeatedly and get at most {@link size} amount of keys in a single call. Can + * be faster than repeated {@link next()} calls. The natural end of the iterator will + * be signaled by yielding an empty array. + * + * @param size Get at most this many keys. Has a soft minimum of 1. + * @param options Options (none at the moment, reserved for future use). + * @param callback Error-first callback. If none is provided, a promise is returned. + */ + nextv (size: number, options: {}, callback: NodeCallback<[K]>): void + nextv (size: number, callback: NodeCallback<[K]>): void + nextv (size: number, options: {}): Promise<[K]> + nextv (size: number): Promise<[K]> + + /** + * Advance repeatedly and get all (remaining) keys as an array, automatically closing + * the iterator. Assumes that those keys fit in memory. If that's not the case, instead + * use {@link next()}, {@link nextv()} or [`for await...of`][1]. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of + * + * @param options Options (none at the moment, reserved for future use). + * @param callback Error-first callback. If none is provided, a promise is returned. + */ + all (options: {}, callback: NodeCallback<[K]>): void + all (callback: NodeCallback<[K]>): void + all (options: {}): Promise<[K]> + all (): Promise<[K]> + + /** + * Seek to the key closest to {@link target}. Subsequent calls to {@link next()}, + * {@link nextv()} or {@link all()} (including implicit calls in a `for await...of` + * loop) will yield keys equal to or larger than {@link target}, or equal to or smaller + * than {@link target} if the {@link AbstractKeyIteratorOptions.reverse} option was + * true. + */ + seek (target: K): void + seek (target: TTarget, options: AbstractSeekOptions): void +} + +export class AbstractValueIterator extends CommonIterator { + constructor (db: TDatabase, options: AbstractValueIteratorOptions) + + /** + * Advance to the next value and yield that value. When possible, prefer + * to use [`for await...of`][1] instead. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of + */ + next (): Promise + next (callback: NodeCallback): void + + /** + * Advance repeatedly and get at most {@link size} amount of values in a single call. + * Can be faster than repeated {@link next()} calls. The natural end of the iterator + * will be signaled by yielding an empty array. + * + * @param size Get at most this many values. Has a soft minimum of 1. + * @param options Options (none at the moment, reserved for future use). + * @param callback Error-first callback. If none is provided, a promise is returned. + */ + nextv (size: number, options: {}, callback: NodeCallback<[V]>): void + nextv (size: number, callback: NodeCallback<[V]>): void + nextv (size: number, options: {}): Promise<[V]> + nextv (size: number): Promise<[V]> + + /** + * Advance repeatedly and get all (remaining) values as an array, automatically closing + * the iterator. Assumes that those values fit in memory. If that's not the case, + * instead use {@link next()}, {@link nextv()} or [`for await...of`][1]. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of + * + * @param options Options (none at the moment, reserved for future use). + * @param callback Error-first callback. If none is provided, a promise is returned. + */ + all (options: {}, callback: NodeCallback<[V]>): void + all (callback: NodeCallback<[V]>): void + all (options: {}): Promise<[V]> + all (): Promise<[V]> + + /** + * Seek to the key closest to {@link target}. Subsequent calls to {@link next()}, + * {@link nextv()} or {@link all()} (including implicit calls in a `for await...of` + * loop) will yield the values of keys equal to or larger than {@link target}, or equal + * to or smaller than {@link target} if the {@link AbstractValueIteratorOptions.reverse} + * option was true. + */ + seek (target: K): void + seek (target: TTarget, options: AbstractSeekOptions): void +} + +/** + * Options for the {@link AbstractIterator.seek} method. + */ +export interface AbstractSeekOptions { + /** + * Custom key encoding, used to encode the `target`. By default the keyEncoding option + * of the iterator is used, or (if that wasn't set) the keyEncoding of the database. + */ + keyEncoding?: string | Transcoder.PartialEncoder | undefined +} + +declare type NextCallback = + (err: Error | undefined | null, key?: K | undefined, value?: V | undefined) => void diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/abstract-level.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/abstract-level.d.ts new file mode 100644 index 0000000..6a09b05 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/abstract-level.d.ts @@ -0,0 +1,485 @@ +import { IManifest } from 'level-supports' +import * as Transcoder from 'level-transcoder' +import { EventEmitter } from 'events' +import { AbstractChainedBatch } from './abstract-chained-batch' +import { AbstractSublevel, AbstractSublevelOptions } from './abstract-sublevel' + +import { + AbstractIterator, + AbstractIteratorOptions, + AbstractKeyIterator, + AbstractKeyIteratorOptions, + AbstractValueIterator, + AbstractValueIteratorOptions +} from './abstract-iterator' + +import { NodeCallback, RangeOptions } from './interfaces' + +/** + * Abstract class for a lexicographically sorted key-value database. + * + * @template TFormat The type used internally by the database to store data. + * @template KDefault The default type of keys if not overridden on operations. + * @template VDefault The default type of values if not overridden on operations. + */ +declare class AbstractLevel + extends EventEmitter { + /** + * Private database constructor. + * + * @param manifest A [manifest](https://github.com/Level/supports) describing the + * features supported by (the private API of) this database. + * @param options Options, of which some will be forwarded to {@link open}. + */ + constructor ( + manifest: Partial, + options?: AbstractDatabaseOptions | undefined + ) + + /** + * A [manifest](https://github.com/Level/supports) describing the features + * supported by this database. + */ + supports: IManifest + + /** + * Read-only getter that returns a string reflecting the current state of the database: + * + * - `'opening'` - waiting for the database to be opened + * - `'open'` - successfully opened the database + * - `'closing'` - waiting for the database to be closed + * - `'closed'` - successfully closed the database. + */ + get status (): 'opening' | 'open' | 'closing' | 'closed' + + /** + * Open the database. + */ + open (): Promise + open (options: AbstractOpenOptions): Promise + open (callback: NodeCallback): void + open (options: AbstractOpenOptions, callback: NodeCallback): void + + /** + * Close the database. + */ + close (): Promise + close (callback: NodeCallback): void + + /** + * Get a value from the database by {@link key}. + */ + get (key: KDefault): Promise + get (key: KDefault, callback: NodeCallback): void + + get ( + key: K, + options: AbstractGetOptions + ): Promise + + get ( + key: K, + options: AbstractGetOptions, + callback: NodeCallback + ): void + + /** + * Get multiple values from the database by an array of {@link keys}. + */ + getMany (keys: KDefault[]): Promise + getMany (keys: KDefault[], callback: NodeCallback): void + + getMany ( + keys: K[], + options: AbstractGetManyOptions + ): Promise + + getMany ( + keys: K[], + options: AbstractGetManyOptions, + callback: NodeCallback + ): void + + /** + * Add a new entry or overwrite an existing entry. + */ + put (key: KDefault, value: VDefault): Promise + put (key: KDefault, value: VDefault, callback: NodeCallback): void + + put ( + key: K, + value: V, + options: AbstractPutOptions + ): Promise + + put ( + key: K, + value: V, + options: AbstractPutOptions, + callback: NodeCallback + ): void + + /** + * Delete an entry by {@link key}. + */ + del (key: KDefault): Promise + del (key: KDefault, callback: NodeCallback): void + + del ( + key: K, + options: AbstractDelOptions + ): Promise + + del ( + key: K, + options: AbstractDelOptions, + callback: NodeCallback + ): void + + /** + * Perform multiple _put_ and/or _del_ operations in bulk. + */ + batch ( + operations: Array> + ): Promise + + batch ( + operations: Array>, + callback: NodeCallback + ): void + + batch ( + operations: Array>, + options: AbstractBatchOptions + ): Promise + + batch ( + operations: Array>, + options: AbstractBatchOptions, + callback: NodeCallback + ): void + + batch (): AbstractChainedBatch + + /** + * Create an iterator. For example: + * + * ```js + * for await (const [key, value] of db.iterator({ gte: 'a' })) { + * console.log([key, value]) + * } + * ``` + */ + iterator (): AbstractIterator + iterator ( + options: AbstractIteratorOptions + ): AbstractIterator + + /** + * Create a key iterator. For example: + * + * ```js + * for await (const key of db.keys({ gte: 'a' })) { + * console.log(key) + * } + * ``` + */ + keys (): AbstractKeyIterator + keys ( + options: AbstractKeyIteratorOptions + ): AbstractKeyIterator + + /** + * Create a value iterator. For example: + * + * ```js + * for await (const value of db.values({ gte: 'a' })) { + * console.log(value) + * } + * ``` + */ + values (): AbstractValueIterator + values ( + options: AbstractValueIteratorOptions + ): AbstractValueIterator + + /** + * Delete all entries or a range. + */ + clear (): Promise + clear (callback: NodeCallback): void + clear (options: AbstractClearOptions): Promise + clear (options: AbstractClearOptions, callback: NodeCallback): void + + /** + * Create a sublevel. + * @param name Name of the sublevel, used to prefix keys. + */ + sublevel (name: string): AbstractSublevel + sublevel ( + name: string, + options: AbstractSublevelOptions + ): AbstractSublevel + + /** + * Add sublevel prefix to the given {@link key}, which must be already-encoded. If this + * database is not a sublevel, the given {@link key} is returned as-is. + * + * @param key Key to add prefix to. + * @param keyFormat Format of {@link key}. One of `'utf8'`, `'buffer'`, `'view'`. + * If `'utf8'` then {@link key} must be a string and the return value will be a string. + * If `'buffer'` then Buffer, if `'view'` then Uint8Array. + */ + prefixKey (key: string, keyFormat: 'utf8'): string + prefixKey (key: Buffer, keyFormat: 'buffer'): Buffer + prefixKey (key: Uint8Array, keyFormat: 'view'): Uint8Array + + /** + * Returns the given {@link encoding} argument as a normalized encoding object + * that follows the [`level-transcoder`](https://github.com/Level/transcoder) + * encoding interface. + */ + keyEncoding ( + encoding: N + ): Transcoder.KnownEncoding + + keyEncoding ( + encoding: Transcoder.MixedEncoding + ): Transcoder.Encoding + + /** + * Returns the default key encoding of the database as a normalized encoding + * object that follows the [`level-transcoder`](https://github.com/Level/transcoder) + * encoding interface. + */ + keyEncoding (): Transcoder.Encoding + + /** + * Returns the given {@link encoding} argument as a normalized encoding object + * that follows the [`level-transcoder`](https://github.com/Level/transcoder) + * encoding interface. + */ + valueEncoding ( + encoding: N + ): Transcoder.KnownEncoding + + valueEncoding ( + encoding: Transcoder.MixedEncoding + ): Transcoder.Encoding + + /** + * Returns the default value encoding of the database as a normalized encoding + * object that follows the [`level-transcoder`](https://github.com/Level/transcoder) + * encoding interface. + */ + valueEncoding (): Transcoder.Encoding + + /** + * Call the function {@link fn} at a later time when {@link status} changes to + * `'open'` or `'closed'`. + */ + defer (fn: Function): void + + /** + * Schedule the function {@link fn} to be called in a next tick of the JavaScript + * event loop, using a microtask scheduler. It will be called with the provided + * {@link args}. + */ + nextTick (fn: Function, ...args: any[]): void +} + +export { AbstractLevel } + +/** + * Options for the database constructor. + */ +export interface AbstractDatabaseOptions + extends Omit { + /** + * Encoding to use for keys. + * @defaultValue `'utf8'` + */ + keyEncoding?: string | Transcoder.PartialEncoding | undefined + + /** + * Encoding to use for values. + * @defaultValue `'utf8'` + */ + valueEncoding?: string | Transcoder.PartialEncoding | undefined +} + +/** + * Options for the {@link AbstractLevel.open} method. + */ +export interface AbstractOpenOptions { + /** + * If `true`, create an empty database if one doesn't already exist. If `false` + * and the database doesn't exist, opening will fail. + * + * @defaultValue `true` + */ + createIfMissing?: boolean | undefined + + /** + * If `true` and the database already exists, opening will fail. + * + * @defaultValue `false` + */ + errorIfExists?: boolean | undefined + + /** + * Wait for, but do not initiate, opening of the database. + * + * @defaultValue `false` + */ + passive?: boolean | undefined +} + +/** + * Options for the {@link AbstractLevel.get} method. + */ +export interface AbstractGetOptions { + /** + * Custom key encoding for this operation, used to encode the `key`. + */ + keyEncoding?: string | Transcoder.PartialEncoder | undefined + + /** + * Custom value encoding for this operation, used to decode the value. + */ + valueEncoding?: string | Transcoder.PartialDecoder | undefined +} + +/** + * Options for the {@link AbstractLevel.getMany} method. + */ +export interface AbstractGetManyOptions { + /** + * Custom key encoding for this operation, used to encode the `keys`. + */ + keyEncoding?: string | Transcoder.PartialEncoder | undefined + + /** + * Custom value encoding for this operation, used to decode values. + */ + valueEncoding?: string | Transcoder.PartialDecoder | undefined +} + +/** + * Options for the {@link AbstractLevel.put} method. + */ +export interface AbstractPutOptions { + /** + * Custom key encoding for this operation, used to encode the `key`. + */ + keyEncoding?: string | Transcoder.PartialEncoder | undefined + + /** + * Custom value encoding for this operation, used to encode the `value`. + */ + valueEncoding?: string | Transcoder.PartialEncoder | undefined +} + +/** + * Options for the {@link AbstractLevel.del} method. + */ +export interface AbstractDelOptions { + /** + * Custom key encoding for this operation, used to encode the `key`. + */ + keyEncoding?: string | Transcoder.PartialEncoder | undefined +} + +/** + * Options for the {@link AbstractLevel.batch} method. + */ +export interface AbstractBatchOptions { + /** + * Custom key encoding for this batch, used to encode keys. + */ + keyEncoding?: string | Transcoder.PartialEncoder | undefined + + /** + * Custom value encoding for this batch, used to encode values. + */ + valueEncoding?: string | Transcoder.PartialEncoder | undefined +} + +/** + * A _put_ or _del_ operation to be committed with the {@link AbstractLevel.batch} + * method. + */ +export type AbstractBatchOperation = + AbstractBatchPutOperation | AbstractBatchDelOperation + +/** + * A _put_ operation to be committed with the {@link AbstractLevel.batch} method. + */ +export interface AbstractBatchPutOperation { + type: 'put' + key: K + value: V + + /** + * Custom key encoding for this _put_ operation, used to encode the {@link key}. + */ + keyEncoding?: string | Transcoder.PartialEncoding | undefined + + /** + * Custom key encoding for this _put_ operation, used to encode the {@link value}. + */ + valueEncoding?: string | Transcoder.PartialEncoding | undefined + + /** + * Act as though the _put_ operation is performed on the given sublevel, to similar + * effect as: + * + * ```js + * await sublevel.batch([{ type: 'put', key, value }]) + * ``` + * + * This allows atomically committing data to multiple sublevels. The {@link key} will + * be prefixed with the `prefix` of the sublevel, and the {@link key} and {@link value} + * will be encoded by the sublevel (using the default encodings of the sublevel unless + * {@link keyEncoding} and / or {@link valueEncoding} are provided). + */ + sublevel?: AbstractSublevel | undefined +} + +/** + * A _del_ operation to be committed with the {@link AbstractLevel.batch} method. + */ +export interface AbstractBatchDelOperation { + type: 'del' + key: K + + /** + * Custom key encoding for this _del_ operation, used to encode the {@link key}. + */ + keyEncoding?: string | Transcoder.PartialEncoding | undefined + + /** + * Act as though the _del_ operation is performed on the given sublevel, to similar + * effect as: + * + * ```js + * await sublevel.batch([{ type: 'del', key }]) + * ``` + * + * This allows atomically committing data to multiple sublevels. The {@link key} will + * be prefixed with the `prefix` of the sublevel, and the {@link key} will be encoded + * by the sublevel (using the default key encoding of the sublevel unless + * {@link keyEncoding} is provided). + */ + sublevel?: AbstractSublevel | undefined +} + +/** + * Options for the {@link AbstractLevel.clear} method. + */ +export interface AbstractClearOptions extends RangeOptions { + /** + * Custom key encoding for this operation, used to encode range options. + */ + keyEncoding?: string | Transcoder.PartialEncoding | undefined +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/abstract-sublevel.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/abstract-sublevel.d.ts new file mode 100644 index 0000000..8b26364 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/abstract-sublevel.d.ts @@ -0,0 +1,58 @@ +import * as Transcoder from 'level-transcoder' +import { AbstractLevel } from './abstract-level' + +/** + * @template TDatabase Type of parent database. + * @template TFormat The type used internally by the parent database to store data. + * @template KDefault The default type of keys if not overridden on operations. + * @template VDefault The default type of values if not overridden on operations. + */ +declare class AbstractSublevel + extends AbstractLevel { + /** + * Sublevel constructor. + * + * @param db Parent database. + * @param name Name of the sublevel, used to prefix keys. + */ + constructor ( + db: TDatabase, + name: string, + options?: AbstractSublevelOptions | undefined + ) + + /** + * Prefix of the sublevel. A read-only string property. + */ + get prefix (): string + + /** + * Parent database. A read-only property. + */ + get db (): TDatabase +} + +/** + * Options for the {@link AbstractLevel.sublevel} method. + */ +export interface AbstractSublevelOptions { + /** + * Character for separating sublevel names from user keys and each other. Must sort + * before characters used in `name`. An error will be thrown if that's not the case. + * + * @defaultValue `'!'` + */ + separator?: string | undefined + + /** + * Encoding to use for keys. + * @defaultValue `'utf8'` + */ + keyEncoding?: string | Transcoder.PartialEncoding | undefined + + /** + * Encoding to use for values. + * @defaultValue `'utf8'` + */ + valueEncoding?: string | Transcoder.PartialEncoding | undefined +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/interfaces.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/interfaces.d.ts new file mode 100644 index 0000000..7b15f8c --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/abstract-level/types/interfaces.d.ts @@ -0,0 +1,14 @@ +/** + * An error-first callback in the style of Node.js. + */ +export type NodeCallback = + (err: Error | undefined | null, result?: T | undefined) => void + +export interface RangeOptions { + gt?: K + gte?: K + lt?: K + lte?: K + reverse?: boolean | undefined + limit?: number | undefined +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/base64-js/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/base64-js/LICENSE new file mode 100644 index 0000000..6d52b8a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/base64-js/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Jameson Little + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/base64-js/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/base64-js/README.md new file mode 100644 index 0000000..b42a48f --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/base64-js/README.md @@ -0,0 +1,34 @@ +base64-js +========= + +`base64-js` does basic base64 encoding/decoding in pure JS. + +[![build status](https://secure.travis-ci.org/beatgammit/base64-js.png)](http://travis-ci.org/beatgammit/base64-js) + +Many browsers already have base64 encoding/decoding functionality, but it is for text data, not all-purpose binary data. + +Sometimes encoding/decoding binary data in the browser is useful, and that is what this module does. + +## install + +With [npm](https://npmjs.org) do: + +`npm install base64-js` and `var base64js = require('base64-js')` + +For use in web browsers do: + +`` + +[Get supported base64-js with the Tidelift Subscription](https://tidelift.com/subscription/pkg/npm-base64-js?utm_source=npm-base64-js&utm_medium=referral&utm_campaign=readme) + +## methods + +`base64js` has three exposed functions, `byteLength`, `toByteArray` and `fromByteArray`, which both take a single argument. + +* `byteLength` - Takes a base64 string and returns length of byte array +* `toByteArray` - Takes a base64 string and returns a byte array +* `fromByteArray` - Takes a byte array and returns a base64 string + +## license + +MIT diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/base64-js/base64js.min.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/base64-js/base64js.min.js new file mode 100644 index 0000000..908ac83 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/base64-js/base64js.min.js @@ -0,0 +1 @@ +(function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"==typeof window?"undefined"==typeof global?"undefined"==typeof self?this:self:global:window,b.base64js=a()}})(function(){return function(){function b(d,e,g){function a(j,i){if(!e[j]){if(!d[j]){var f="function"==typeof require&&require;if(!i&&f)return f(j,!0);if(h)return h(j,!0);var c=new Error("Cannot find module '"+j+"'");throw c.code="MODULE_NOT_FOUND",c}var k=e[j]={exports:{}};d[j][0].call(k.exports,function(b){var c=d[j][1][b];return a(c||b)},k,k.exports,b,d,e,g)}return e[j].exports}for(var h="function"==typeof require&&require,c=0;c>16,j[k++]=255&b>>8,j[k++]=255&b;return 2===h&&(b=l[a.charCodeAt(c)]<<2|l[a.charCodeAt(c+1)]>>4,j[k++]=255&b),1===h&&(b=l[a.charCodeAt(c)]<<10|l[a.charCodeAt(c+1)]<<4|l[a.charCodeAt(c+2)]>>2,j[k++]=255&b>>8,j[k++]=255&b),j}function g(a){return k[63&a>>18]+k[63&a>>12]+k[63&a>>6]+k[63&a]}function h(a,b,c){for(var d,e=[],f=b;fj?j:g+f));return 1===d?(b=a[c-1],e.push(k[b>>2]+k[63&b<<4]+"==")):2===d&&(b=(a[c-2]<<8)+a[c-1],e.push(k[b>>10]+k[63&b>>4]+k[63&b<<2]+"=")),e.join("")}c.byteLength=function(a){var b=d(a),c=b[0],e=b[1];return 3*(c+e)/4-e},c.toByteArray=f,c.fromByteArray=j;for(var k=[],l=[],m="undefined"==typeof Uint8Array?Array:Uint8Array,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",o=0,p=n.length;o 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // Trim off extra bytes after placeholder bytes are found + // See: https://github.com/beatgammit/base64-js/issues/42 + var validLen = b64.indexOf('=') + if (validLen === -1) validLen = len + + var placeHoldersLen = validLen === len + ? 0 + : 4 - (validLen % 4) + + return [validLen, placeHoldersLen] +} + +// base64 is 4/3 + up to two characters of the original data +function byteLength (b64) { + var lens = getLens(b64) + var validLen = lens[0] + var placeHoldersLen = lens[1] + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen +} + +function _byteLength (b64, validLen, placeHoldersLen) { + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen +} + +function toByteArray (b64) { + var tmp + var lens = getLens(b64) + var validLen = lens[0] + var placeHoldersLen = lens[1] + + var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)) + + var curByte = 0 + + // if there are placeholders, only get up to the last complete 4 chars + var len = placeHoldersLen > 0 + ? validLen - 4 + : validLen + + var i + for (i = 0; i < len; i += 4) { + tmp = + (revLookup[b64.charCodeAt(i)] << 18) | + (revLookup[b64.charCodeAt(i + 1)] << 12) | + (revLookup[b64.charCodeAt(i + 2)] << 6) | + revLookup[b64.charCodeAt(i + 3)] + arr[curByte++] = (tmp >> 16) & 0xFF + arr[curByte++] = (tmp >> 8) & 0xFF + arr[curByte++] = tmp & 0xFF + } + + if (placeHoldersLen === 2) { + tmp = + (revLookup[b64.charCodeAt(i)] << 2) | + (revLookup[b64.charCodeAt(i + 1)] >> 4) + arr[curByte++] = tmp & 0xFF + } + + if (placeHoldersLen === 1) { + tmp = + (revLookup[b64.charCodeAt(i)] << 10) | + (revLookup[b64.charCodeAt(i + 1)] << 4) | + (revLookup[b64.charCodeAt(i + 2)] >> 2) + arr[curByte++] = (tmp >> 8) & 0xFF + arr[curByte++] = tmp & 0xFF + } + + return arr +} + +function tripletToBase64 (num) { + return lookup[num >> 18 & 0x3F] + + lookup[num >> 12 & 0x3F] + + lookup[num >> 6 & 0x3F] + + lookup[num & 0x3F] +} + +function encodeChunk (uint8, start, end) { + var tmp + var output = [] + for (var i = start; i < end; i += 3) { + tmp = + ((uint8[i] << 16) & 0xFF0000) + + ((uint8[i + 1] << 8) & 0xFF00) + + (uint8[i + 2] & 0xFF) + output.push(tripletToBase64(tmp)) + } + return output.join('') +} + +function fromByteArray (uint8) { + var tmp + var len = uint8.length + var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes + var parts = [] + var maxChunkLength = 16383 // must be multiple of 3 + + // go through the array every three bytes, we'll deal with trailing stuff later + for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { + parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + if (extraBytes === 1) { + tmp = uint8[len - 1] + parts.push( + lookup[tmp >> 2] + + lookup[(tmp << 4) & 0x3F] + + '==' + ) + } else if (extraBytes === 2) { + tmp = (uint8[len - 2] << 8) + uint8[len - 1] + parts.push( + lookup[tmp >> 10] + + lookup[(tmp >> 4) & 0x3F] + + lookup[(tmp << 2) & 0x3F] + + '=' + ) + } + + return parts.join('') +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/base64-js/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/base64-js/package.json new file mode 100644 index 0000000..c3972e3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/base64-js/package.json @@ -0,0 +1,47 @@ +{ + "name": "base64-js", + "description": "Base64 encoding/decoding in pure JS", + "version": "1.5.1", + "author": "T. Jameson Little ", + "typings": "index.d.ts", + "bugs": { + "url": "https://github.com/beatgammit/base64-js/issues" + }, + "devDependencies": { + "babel-minify": "^0.5.1", + "benchmark": "^2.1.4", + "browserify": "^16.3.0", + "standard": "*", + "tape": "4.x" + }, + "homepage": "https://github.com/beatgammit/base64-js", + "keywords": [ + "base64" + ], + "license": "MIT", + "main": "index.js", + "repository": { + "type": "git", + "url": "git://github.com/beatgammit/base64-js.git" + }, + "scripts": { + "build": "browserify -s base64js -r ./ | minify > base64js.min.js", + "lint": "standard", + "test": "npm run lint && npm run unit", + "unit": "tape test/*.js" + }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/CHANGELOG.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/CHANGELOG.md new file mode 100644 index 0000000..800c0ef --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog + +## [1.0.1] - 2022-03-06 + +### Fixed + +- Fix TypeScript type declarations ([`d5db420`](https://github.com/Level/browser-level/commit/d5db420)) (Vincent Weevers) + +## [1.0.0] - 2022-03-05 + +_:seedling: Initial release. If you are upgrading from `level-js`, please see [UPGRADING.md](UPGRADING.md)._ + +[1.0.1]: https://github.com/Level/browser-level/releases/tag/v1.0.1 + +[1.0.0]: https://github.com/Level/browser-level/releases/tag/v1.0.0 diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/LICENSE new file mode 100644 index 0000000..bce8197 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2012 Max Ogden and the contributors to browser-level. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/README.md new file mode 100644 index 0000000..13d7195 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/README.md @@ -0,0 +1,149 @@ +# browser-level + +**An [`abstract-level`][abstract-level] database for browsers, backed by [IndexedDB][indexeddb].** The successor to [`level-js`](https://github.com/Level/level-js). If you are upgrading, please see [UPGRADING.md](UPGRADING.md). + +> :pushpin: Which module should I use? What is `abstract-level`? Head over to the [FAQ](https://github.com/Level/community#faq). + +[![level badge][level-badge]][awesome] +[![npm](https://img.shields.io/npm/v/browser-level.svg)](https://www.npmjs.com/package/browser-level) +[![Test](https://img.shields.io/github/workflow/status/Level/browser-level/Test?label=test)](https://github.com/Level/browser-level/actions/workflows/test.yml) +[![Coverage](https://img.shields.io/codecov/c/github/Level/browser-level?label=\&logo=codecov\&logoColor=fff)](https://codecov.io/gh/Level/browser-level) +[![Standard](https://img.shields.io/badge/standard-informational?logo=javascript\&logoColor=fff)](https://standardjs.com) +[![Common Changelog](https://common-changelog.org/badge.svg)](https://common-changelog.org) +[![Donate](https://img.shields.io/badge/donate-orange?logo=open-collective\&logoColor=fff)](https://opencollective.com/level) + +## Table of Contents + +
Click to expand + +- [Usage](#usage) +- [API](#api) + - [`db = new BrowserLevel(location[, options])`](#db--new-browserlevellocation-options) + - [`BrowserLevel.destroy(location[, prefix][, callback])`](#browserleveldestroylocation-prefix-callback) +- [Install](#install) +- [Contributing](#contributing) +- [Donate](#donate) +- [License](#license) + +
+ +## Usage + +```js +const { BrowserLevel } = require('browser-level') + +// Create a database called 'example' +const db = new BrowserLevel('example', { valueEncoding: 'json' }) + +// Add an entry with key 'a' and value 1 +await db.put('a', 1) + +// Add multiple entries +await db.batch([{ type: 'put', key: 'b', value: 2 }]) + +// Get value of key 'a': 1 +const value = await db.get('a') + +// Iterate entries with keys that are greater than 'a' +for await (const [key, value] of db.iterator({ gt: 'a' })) { + console.log(value) // 2 +} +``` + + + + + +## API + +The API of `browser-level` follows that of [`abstract-level`](https://github.com/Level/abstract-level) with just two additional constructor options (see below) and one additional method (see below). As such, the majority of the API is documented in `abstract-level`. The `createIfMissing` and `errorIfExists` options of `abstract-level` are not supported here. + +Like other implementations of `abstract-level`, `browser-level` has first-class support of binary keys and values, using either [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) or [Buffer](https://nodejs.org/api/buffer.html). In order to sort string and binary keys the same way as other databases, `browser-level` internally converts data to a Uint8Array before passing them to IndexedDB. If you have no need to work with `Buffer` keys or values, you can choose to omit the [`buffer`](https://github.com/feross/buffer) shim from a JavaScript bundle (through configuration of Webpack, Browserify or other bundlers). + +Due to limitations of IndexedDB, `browser-level` does not offer snapshot guarantees. Such a guarantee would mean that an iterator does not see the data of simultaneous writes - it would be reading from a snapshot in time. In contrast, a `browser-level` iterator reads a few entries ahead and then opens a new IndexedDB transaction on the next read. A "few" means all entries for `iterator.all()`, `size` amount of entries for `iterator.nextv(size)` and a hardcoded 100 entries for `iterator.next()`. Individual calls to those methods have snapshot guarantees but repeated calls do not. + +The result is that an iterator will include the data of simultaneous writes, if `db.put()`, `db.del()` or `db.batch()` are called in between creating the iterator and consuming the iterator, or in between calls to `iterator.next()` or `iterator.nextv()`. For example: + +```js +const iterator = db.iterator() +await db.put('abc', '123') + +for await (const [key, value] of iterator) { + // This might be 'abc' + console.log(key) +} +``` + +If snapshot guarantees are a must for your application then use `iterator.all()` and call it immediately after creating the iterator: + +```js +const entries = await db.iterator({ limit: 50 }).all() + +// Synchronously iterate the result +for (const [key, value] of entries) { + console.log(key) +} +``` + +### `db = new BrowserLevel(location[, options])` + +Create a new database or open an existing one. The required `location` argument is the string name of the [`IDBDatabase`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase) to be opened, as well as the name of the object store within that database. The name of the `IDBDatabase` will be prefixed with `options.prefix`. + +Besides `abstract-level` options, the optional `options` argument may contain: + +- `prefix` (string, default: `'level-js-'`): Prefix for the `IDBDatabase` name. Can be set to an empty string. The default is compatible with `level-js`. +- `version` (string or number, default: `1`): The version to open the `IDBDatabase` with. + +See [`IDBFactory#open()`](https://developer.mozilla.org/en-US/docs/Web/API/IDBFactory/open) for more details about database name and version. + +### `BrowserLevel.destroy(location[, prefix][, callback])` + +Delete the IndexedDB database at the given `location`. If `prefix` is not given, it defaults to the same value as the `BrowserLevel` constructor does. The `callback` function will be called when the destroy operation is complete, with a possible error argument. If no callback is provided, a promise is returned. This method is an additional method that is not part of the [`abstract-level`](https://github.com/Level/abstract-level) interface. + +Before calling `destroy()`, close a database if it's using the same `location` and `prefix`: + +```js +const db = new BrowserLevel('example') +await db.close() +await BrowserLevel.destroy('example') +``` + +## Install + +With [npm](https://npmjs.org) do: + +```bash +npm install browser-level +``` + +This module is best used with [`browserify`](http://browserify.org) or similar bundlers. + + + +## Contributing + +[`Level/browser-level`](https://github.com/Level/browser-level) is an **OPEN Open Source Project**. This means that: + +> Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project. + +See the [Contribution Guide](https://github.com/Level/community/blob/master/CONTRIBUTING.md) for more details. + +## Donate + +Support us with a monthly donation on [Open Collective](https://opencollective.com/level) and help us continue our work. + +## License + +[MIT](LICENSE) + +[level-badge]: https://leveljs.org/img/badge.svg + +[indexeddb]: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API + +[awesome]: https://github.com/Level/awesome + +[abstract-level]: https://github.com/Level/abstract-level diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/UPGRADING.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/UPGRADING.md new file mode 100644 index 0000000..5762168 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/UPGRADING.md @@ -0,0 +1,135 @@ +# Upgrade Guide + +This document describes breaking changes and how to upgrade. For a complete list of changes including minor and patch releases, please refer to the [changelog](CHANGELOG.md). + +## 1.0.0 + +**Introducing `browser-level`: a fork of [`level-js`](https://github.com/Level/level-js) that removes the need for [`levelup`](https://github.com/Level/levelup) and more. It implements the [`abstract-level`](https://github.com/Level/abstract-level) interface instead of [`abstract-leveldown`](https://github.com/Level/abstract-leveldown) and thus has the same API as `level` and `levelup` including encodings, promises and events. In addition, you can now choose to use Uint8Array instead of Buffer. Sublevels are builtin.** + +We've put together several upgrade guides for different modules. See the [FAQ](https://github.com/Level/community#faq) to find the best upgrade guide for you. This one describes how to replace `level-js` with `browser-level`. There will be a separate guide for `level`. + +### What is not covered here + +If you are using any of the following, please also read the upgrade guide of [`abstract-level@1`](https://github.com/Level/abstract-level/blob/main/UPGRADING.md#100) which goes into more detail about these: + +- Specific error messages (replaced with error codes) +- The `db.iterator().end()` method (renamed to `close()`, with `end()` as an alias) +- Zero-length keys and range options (now valid) +- The `db.supports.bufferKeys` property. + +### Changes to initialization + +We started using classes, which means using `new` is now required. If you previously did: + +```js +const levelJs = require('level-js') +const db = levelJs('example') +``` + +You must now do: + +```js +const { BrowserLevel } = require('browser-level') +const db = new BrowserLevel('example') +``` + +Arguments and options are the same except that `browser-level` has additional options to control encodings. For backwards compatibility, the `prefix` option has the same default value. Namely `'level-js-'`. + +### There is only encodings + +The `asBuffer`, `valueAsBuffer` and `keyAsBuffer` options have been replaced with encoding options. The default encoding is `'utf8'` which means operations return strings rather than Buffers by default. If you previously did: + +```js +db.get('example', { asBuffer: false }, callback) +db.get('example', callback) +``` + +You must now do: + +```js +db.get('example', callback) +db.get('example', { valueEncoding: 'buffer' }, callback) +``` + +Or using promises (new): + +```js +const str = await db.get('example') +const buf = await db.get('example', { valueEncoding: 'buffer' }) +``` + +Or using Uint8Array (new): + +```js +const arr = await db.get('example', { valueEncoding: 'view' }) +``` + +### Uint8Array support + +A `browser-level` database uses Uint8Array internally, instead of Buffer like `level-js` did. This doesn't change the ability to read existing databases: `browser-level` can read and write databases that were created with `level-js` and vice versa. Externally both Uint8Array and Buffer can be used (see README for details) to maintain backwards- and ecosystem compatibility. You can choose to use Uint8Array exclusively and omit the `buffer` shim from a JavaScript bundle (through configuration of Webpack, Browserify or other bundlers). + +### Unwrapping the onion + +If you were wrapping `level-js` with `levelup`, `encoding-down` and / or `subleveldown`, remove those modules. If you previously did: + +```js +const levelJs = require('level-js') +const levelup = require('levelup') +const enc = require('encoding-down') +const subleveldown = require('subleveldown') + +const db = levelup(enc(levelJs('example'))) +const sublevel = subleveldown(db, 'foo') +``` + +You must now do: + +```js +const { BrowserLevel } = require('browser-level') +const db = new BrowserLevel('example') +const sublevel = db.sublevel('foo') +``` + +### Prefers backpressure over snapshot guarantees + +Previously (in `level-js`) an iterator would keep reading in the background, so as to keep the underlying IndexedDB transaction alive and thus not see the data of simultaneous writes. I.e. it was reading from a snapshot in time. This had two major downsides. It was not possible to lazily iterate a large database, as all of the data would be read into memory. Secondly, IndexedDB doesn't actually use a snapshot (the Chrome implementation used to, others never did) but rather a blocking transaction. Meaning you couldn't write to a database while an iterator was active; the write would wait for the iterator to end. + +To solve those issues, an iterator now reads a few entries ahead and then opens a new transaction on the next read. A "few" means all entries for `iterator.all()`, `size` amount of entries for `iterator.nextv(size)` and a hardcoded 100 entries for `iterator.next()`. Individual calls to those methods still have snapshot guarantees, but repeated calls do not. + +The resulting breaking change is that an iterator will include the data of simultaneous writes, if `db.put()`, `db.del()` or `db.batch()` are called in between creating the iterator and consuming the iterator, or in between calls to `iterator.next()` or `iterator.nextv()`. For example: + +```js +const iterator = db.iterator() +await db.put('abc', '123') + +for await (const [key, value] of iterator) { + // This might be 'abc' + console.log(key) +} +``` + +To reflect the new behavior, `db.supports.snapshots` is now false. If snapshot guarantees are a must for your application then use `iterator.all()` (which is a new method compared to `level-js`) and call it immediately after creating the iterator: + +```js +const entries = await db.iterator({ limit: 50 }).all() + +// Synchronously iterate the result +for (const [key, value] of entries) { + console.log(key) +} +``` + +Unrelated, iterating should now be faster because `browser-level` iterators use the `getAll()` and `getAllKeys()` methods of IndexedDB, instead of a cursor like `level-js` did. This means multiple entries are transferred from IndexedDB to JS in a single turn of the JS event loop, rather than one turn per entry. Reverse iterators do still use a cursor and are therefor slower. + +Lastly, the new logic enabled us to implement and fully support `iterator.seek()`. + +### Changes to lesser-used properties and methods + +- The `db.prefix` property of `level-js`, conflicting with the `db.prefix` property of sublevels, has been renamed to `db.namePrefix`. The relevant constructor option (`prefix`) remains the same. +- The `db.location`, `db.version` and `db.db` properties are now read-only getters +- The internal `db.store()` and `db.await()` methods are no longer accessible +- The `db.upgrade()` utility for upgrading from v4.x to v5.x has been removed. + +--- + +_For earlier releases, before `browser-level` was forked from `level-js` (v6.1.0), please see [the upgrade guide of `level-js`](https://github.com/Level/level-js/blob/HEAD/UPGRADING.md)._ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/index.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/index.d.ts new file mode 100644 index 0000000..1cdbc92 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/index.d.ts @@ -0,0 +1,111 @@ +import { + AbstractLevel, + AbstractDatabaseOptions, + NodeCallback, + AbstractOpenOptions, + AbstractGetOptions, + AbstractGetManyOptions, + AbstractPutOptions, + AbstractDelOptions, + AbstractBatchOptions, + AbstractChainedBatch, + AbstractIterator, + AbstractKeyIterator, + AbstractValueIterator, + AbstractIteratorOptions, + AbstractKeyIteratorOptions, + AbstractValueIteratorOptions, + AbstractBatchOperation +} from 'abstract-level' + +/** + * An {@link AbstractLevel} database for browsers, backed by [IndexedDB][1]. + * + * @template KDefault The default type of keys if not overridden on operations. + * @template VDefault The default type of values if not overridden on operations. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API + */ +export class BrowserLevel + extends AbstractLevel { + /** + * Database constructor. + * + * @param location The name of the [`IDBDatabase`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase) + * to be opened, as well as the name of the object store within that database. The name + * of the `IDBDatabase` will be prefixed with {@link DatabaseOptions.prefix}. + * @param options Options, of which some will be forwarded to {@link open}. + */ + constructor (location: string, options?: DatabaseOptions | undefined) + + /** + * Location that was passed to the constructor. + */ + get location (): string + + /** + * Database name prefix that was passed to the constructor (as `prefix`). + */ + get namePrefix (): string + + /** + * Version that was passed to the constructor. + */ + get version (): number + + /** + * Delete the IndexedDB database at the given {@link location}. + */ + static destroy (location: string): Promise + static destroy (location: string, prefix: string): Promise + static destroy (location: string, callback: NodeCallback): void + static destroy (location: string, prefix: string, callback: NodeCallback): void +} + +/** + * Options for the {@link BrowserLevel} constructor. + */ +export interface DatabaseOptions extends AbstractDatabaseOptions { + /** + * Prefix for the `IDBDatabase` name. Can be set to an empty string. + * + * @defaultValue `'level-js-'` + */ + prefix?: string + + /** + * The version to open the `IDBDatabase` with. + * + * @defaultValue `1` + */ + version?: number | string + + /** + * An {@link AbstractLevel} option that has no effect on {@link BrowserLevel}. + */ + createIfMissing?: boolean + + /** + * An {@link AbstractLevel} option that has no effect on {@link BrowserLevel}. + */ + errorIfExists?: boolean +} + +// Export types so that consumers don't have to guess whether they're extended +export type OpenOptions = AbstractOpenOptions +export type GetOptions = AbstractGetOptions +export type GetManyOptions = AbstractGetManyOptions +export type PutOptions = AbstractPutOptions +export type DelOptions = AbstractDelOptions + +export type BatchOptions = AbstractBatchOptions +export type BatchOperation = AbstractBatchOperation +export type ChainedBatch = AbstractChainedBatch + +export type Iterator = AbstractIterator +export type KeyIterator = AbstractKeyIterator +export type ValueIterator = AbstractValueIterator + +export type IteratorOptions = AbstractIteratorOptions +export type KeyIteratorOptions = AbstractKeyIteratorOptions +export type ValueIteratorOptions = AbstractValueIteratorOptions diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/index.js new file mode 100644 index 0000000..43c95a1 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/index.js @@ -0,0 +1,291 @@ +/* global indexedDB */ + +'use strict' + +const { AbstractLevel } = require('abstract-level') +const ModuleError = require('module-error') +const parallel = require('run-parallel-limit') +const { fromCallback } = require('catering') +const { Iterator } = require('./iterator') +const deserialize = require('./util/deserialize') +const clear = require('./util/clear') +const createKeyRange = require('./util/key-range') + +// Keep as-is for compatibility with existing level-js databases +const DEFAULT_PREFIX = 'level-js-' + +const kIDB = Symbol('idb') +const kNamePrefix = Symbol('namePrefix') +const kLocation = Symbol('location') +const kVersion = Symbol('version') +const kStore = Symbol('store') +const kOnComplete = Symbol('onComplete') +const kPromise = Symbol('promise') + +class BrowserLevel extends AbstractLevel { + constructor (location, options, _) { + // To help migrating to abstract-level + if (typeof options === 'function' || typeof _ === 'function') { + throw new ModuleError('The levelup-style callback argument has been removed', { + code: 'LEVEL_LEGACY' + }) + } + + const { prefix, version, ...forward } = options || {} + + super({ + encodings: { view: true }, + snapshots: false, + createIfMissing: false, + errorIfExists: false, + seek: true + }, forward) + + if (typeof location !== 'string') { + throw new Error('constructor requires a location string argument') + } + + // TODO (next major): remove default prefix + this[kLocation] = location + this[kNamePrefix] = prefix == null ? DEFAULT_PREFIX : prefix + this[kVersion] = parseInt(version || 1, 10) + this[kIDB] = null + } + + get location () { + return this[kLocation] + } + + get namePrefix () { + return this[kNamePrefix] + } + + get version () { + return this[kVersion] + } + + // Exposed for backwards compat and unit tests + get db () { + return this[kIDB] + } + + get type () { + return 'browser-level' + } + + _open (options, callback) { + const req = indexedDB.open(this[kNamePrefix] + this[kLocation], this[kVersion]) + + req.onerror = function () { + callback(req.error || new Error('unknown error')) + } + + req.onsuccess = () => { + this[kIDB] = req.result + callback() + } + + req.onupgradeneeded = (ev) => { + const db = ev.target.result + + if (!db.objectStoreNames.contains(this[kLocation])) { + db.createObjectStore(this[kLocation]) + } + } + } + + [kStore] (mode) { + const transaction = this[kIDB].transaction([this[kLocation]], mode) + return transaction.objectStore(this[kLocation]) + } + + [kOnComplete] (request, callback) { + const transaction = request.transaction + + // Take advantage of the fact that a non-canceled request error aborts + // the transaction. I.e. no need to listen for "request.onerror". + transaction.onabort = function () { + callback(transaction.error || new Error('aborted by user')) + } + + transaction.oncomplete = function () { + callback(null, request.result) + } + } + + _get (key, options, callback) { + const store = this[kStore]('readonly') + let req + + try { + req = store.get(key) + } catch (err) { + return this.nextTick(callback, err) + } + + this[kOnComplete](req, function (err, value) { + if (err) return callback(err) + + if (value === undefined) { + return callback(new ModuleError('Entry not found', { + code: 'LEVEL_NOT_FOUND' + })) + } + + callback(null, deserialize(value)) + }) + } + + _getMany (keys, options, callback) { + const store = this[kStore]('readonly') + const tasks = keys.map((key) => (next) => { + let request + + try { + request = store.get(key) + } catch (err) { + return next(err) + } + + request.onsuccess = () => { + const value = request.result + next(null, value === undefined ? value : deserialize(value)) + } + + request.onerror = (ev) => { + ev.stopPropagation() + next(request.error) + } + }) + + parallel(tasks, 16, callback) + } + + _del (key, options, callback) { + const store = this[kStore]('readwrite') + let req + + try { + req = store.delete(key) + } catch (err) { + return this.nextTick(callback, err) + } + + this[kOnComplete](req, callback) + } + + _put (key, value, options, callback) { + const store = this[kStore]('readwrite') + let req + + try { + // Will throw a DataError or DataCloneError if the environment + // does not support serializing the key or value respectively. + req = store.put(value, key) + } catch (err) { + return this.nextTick(callback, err) + } + + this[kOnComplete](req, callback) + } + + // TODO: implement key and value iterators + _iterator (options) { + return new Iterator(this, this[kLocation], options) + } + + _batch (operations, options, callback) { + const store = this[kStore]('readwrite') + const transaction = store.transaction + let index = 0 + let error + + transaction.onabort = function () { + callback(error || transaction.error || new Error('aborted by user')) + } + + transaction.oncomplete = function () { + callback() + } + + // Wait for a request to complete before making the next, saving CPU. + function loop () { + const op = operations[index++] + const key = op.key + + let req + + try { + req = op.type === 'del' ? store.delete(key) : store.put(op.value, key) + } catch (err) { + error = err + transaction.abort() + return + } + + if (index < operations.length) { + req.onsuccess = loop + } else if (typeof transaction.commit === 'function') { + // Commit now instead of waiting for auto-commit + transaction.commit() + } + } + + loop() + } + + _clear (options, callback) { + let keyRange + let req + + try { + keyRange = createKeyRange(options) + } catch (e) { + // The lower key is greater than the upper key. + // IndexedDB throws an error, but we'll just do nothing. + return this.nextTick(callback) + } + + if (options.limit >= 0) { + // IDBObjectStore#delete(range) doesn't have such an option. + // Fall back to cursor-based implementation. + return clear(this, this[kLocation], keyRange, options, callback) + } + + try { + const store = this[kStore]('readwrite') + req = keyRange ? store.delete(keyRange) : store.clear() + } catch (err) { + return this.nextTick(callback, err) + } + + this[kOnComplete](req, callback) + } + + _close (callback) { + this[kIDB].close() + this.nextTick(callback) + } +} + +BrowserLevel.destroy = function (location, prefix, callback) { + if (typeof prefix === 'function') { + callback = prefix + prefix = DEFAULT_PREFIX + } + + callback = fromCallback(callback, kPromise) + const request = indexedDB.deleteDatabase(prefix + location) + + request.onsuccess = function () { + callback() + } + + request.onerror = function (err) { + callback(err) + } + + return callback[kPromise] +} + +exports.BrowserLevel = BrowserLevel diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/iterator.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/iterator.js new file mode 100644 index 0000000..6b84425 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/iterator.js @@ -0,0 +1,238 @@ +'use strict' + +const { AbstractIterator } = require('abstract-level') +const createKeyRange = require('./util/key-range') +const deserialize = require('./util/deserialize') + +const kCache = Symbol('cache') +const kFinished = Symbol('finished') +const kOptions = Symbol('options') +const kCurrentOptions = Symbol('currentOptions') +const kPosition = Symbol('position') +const kLocation = Symbol('location') +const kFirst = Symbol('first') +const emptyOptions = {} + +class Iterator extends AbstractIterator { + constructor (db, location, options) { + super(db, options) + + this[kCache] = [] + this[kFinished] = this.limit === 0 + this[kOptions] = options + this[kCurrentOptions] = { ...options } + this[kPosition] = undefined + this[kLocation] = location + this[kFirst] = true + } + + // Note: if called by _all() then size can be Infinity. This is an internal + // detail; by design AbstractIterator.nextv() does not support Infinity. + _nextv (size, options, callback) { + this[kFirst] = false + + if (this[kFinished]) { + return this.nextTick(callback, null, []) + } else if (this[kCache].length > 0) { + // TODO: mixing next and nextv is not covered by test suite + size = Math.min(size, this[kCache].length) + return this.nextTick(callback, null, this[kCache].splice(0, size)) + } + + // Adjust range by what we already visited + if (this[kPosition] !== undefined) { + if (this[kOptions].reverse) { + this[kCurrentOptions].lt = this[kPosition] + this[kCurrentOptions].lte = undefined + } else { + this[kCurrentOptions].gt = this[kPosition] + this[kCurrentOptions].gte = undefined + } + } + + let keyRange + + try { + keyRange = createKeyRange(this[kCurrentOptions]) + } catch (_) { + // The lower key is greater than the upper key. + // IndexedDB throws an error, but we'll just return 0 results. + this[kFinished] = true + return this.nextTick(callback, null, []) + } + + const transaction = this.db.db.transaction([this[kLocation]], 'readonly') + const store = transaction.objectStore(this[kLocation]) + const entries = [] + + if (!this[kOptions].reverse) { + let keys + let values + + const complete = () => { + // Wait for both requests to complete + if (keys === undefined || values === undefined) return + + const length = Math.max(keys.length, values.length) + + if (length === 0 || size === Infinity) { + this[kFinished] = true + } else { + this[kPosition] = keys[length - 1] + } + + // Resize + entries.length = length + + // Merge keys and values + for (let i = 0; i < length; i++) { + const key = keys[i] + const value = values[i] + + entries[i] = [ + this[kOptions].keys && key !== undefined ? deserialize(key) : undefined, + this[kOptions].values && value !== undefined ? deserialize(value) : undefined + ] + } + + maybeCommit(transaction) + } + + // If keys were not requested and size is Infinity, we don't have to keep + // track of position and can thus skip getting keys. + if (this[kOptions].keys || size < Infinity) { + store.getAllKeys(keyRange, size < Infinity ? size : undefined).onsuccess = (ev) => { + keys = ev.target.result + complete() + } + } else { + keys = [] + this.nextTick(complete) + } + + if (this[kOptions].values) { + store.getAll(keyRange, size < Infinity ? size : undefined).onsuccess = (ev) => { + values = ev.target.result + complete() + } + } else { + values = [] + this.nextTick(complete) + } + } else { + // Can't use getAll() in reverse, so use a slower cursor that yields one item at a time + // TODO: test if all target browsers support openKeyCursor + const method = !this[kOptions].values && store.openKeyCursor ? 'openKeyCursor' : 'openCursor' + + store[method](keyRange, 'prev').onsuccess = (ev) => { + const cursor = ev.target.result + + if (cursor) { + const { key, value } = cursor + this[kPosition] = key + + entries.push([ + this[kOptions].keys && key !== undefined ? deserialize(key) : undefined, + this[kOptions].values && value !== undefined ? deserialize(value) : undefined + ]) + + if (entries.length < size) { + cursor.continue() + } else { + maybeCommit(transaction) + } + } else { + this[kFinished] = true + } + } + } + + // If an error occurs (on the request), the transaction will abort. + transaction.onabort = () => { + callback(transaction.error || new Error('aborted by user')) + callback = null + } + + transaction.oncomplete = () => { + callback(null, entries) + callback = null + } + } + + _next (callback) { + if (this[kCache].length > 0) { + const [key, value] = this[kCache].shift() + this.nextTick(callback, null, key, value) + } else if (this[kFinished]) { + this.nextTick(callback) + } else { + let size = Math.min(100, this.limit - this.count) + + if (this[kFirst]) { + // It's common to only want one entry initially or after a seek() + this[kFirst] = false + size = 1 + } + + this._nextv(size, emptyOptions, (err, entries) => { + if (err) return callback(err) + this[kCache] = entries + this._next(callback) + }) + } + } + + _all (options, callback) { + this[kFirst] = false + + // TODO: mixing next and all is not covered by test suite + const cache = this[kCache].splice(0, this[kCache].length) + const size = this.limit - this.count - cache.length + + if (size <= 0) { + return this.nextTick(callback, null, cache) + } + + this._nextv(size, emptyOptions, (err, entries) => { + if (err) return callback(err) + if (cache.length > 0) entries = cache.concat(entries) + callback(null, entries) + }) + } + + _seek (target, options) { + this[kFirst] = true + this[kCache] = [] + this[kFinished] = false + this[kPosition] = undefined + + // TODO: not covered by test suite + this[kCurrentOptions] = { ...this[kOptions] } + + let keyRange + + try { + keyRange = createKeyRange(this[kOptions]) + } catch (_) { + this[kFinished] = true + return + } + + if (keyRange !== null && !keyRange.includes(target)) { + this[kFinished] = true + } else if (this[kOptions].reverse) { + this[kCurrentOptions].lte = target + } else { + this[kCurrentOptions].gte = target + } + } +} + +exports.Iterator = Iterator + +function maybeCommit (transaction) { + // Commit (meaning close) now instead of waiting for auto-commit + if (typeof transaction.commit === 'function') { + transaction.commit() + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/package.json new file mode 100644 index 0000000..e3b43a3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/package.json @@ -0,0 +1,56 @@ +{ + "name": "browser-level", + "version": "1.0.1", + "description": "An abstract-level database for browsers, backed by IndexedDB", + "author": "max ogden", + "license": "MIT", + "main": "index.js", + "types": "./index.d.ts", + "scripts": { + "test": "standard && ts-standard *.ts && hallmark && airtap -p local --coverage test/index.js && nyc report", + "test-browsers": "standard && airtap --coverage test/index.js", + "coverage": "nyc report -r lcovonly", + "dependency-check": "dependency-check --no-dev .", + "prepublishOnly": "npm run dependency-check" + }, + "files": [ + "index.js", + "index.d.ts", + "iterator.js", + "util", + "CHANGELOG.md", + "UPGRADING.md", + "sauce-labs.svg" + ], + "dependencies": { + "abstract-level": "^1.0.2", + "catering": "^2.1.1", + "module-error": "^1.0.2", + "run-parallel-limit": "^1.1.0" + }, + "devDependencies": { + "@voxpelli/tsconfig": "^3.1.0", + "airtap": "^4.0.4", + "airtap-playwright": "^1.0.1", + "airtap-sauce": "^1.1.0", + "dependency-check": "^4.1.0", + "hallmark": "^4.1.0", + "nyc": "^15.0.0", + "standard": "^16.0.4", + "tape": "^5.5.2", + "ts-standard": "^11.0.0", + "typescript": "^4.5.5", + "uuid": "^3.3.2" + }, + "repository": { + "type": "git", + "url": "https://github.com/Level/browser-level.git" + }, + "homepage": "https://github.com/Level/browser-level", + "keywords": [ + "level", + "leveldb", + "indexeddb", + "abstract-level" + ] +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/sauce-labs.svg b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/sauce-labs.svg new file mode 100644 index 0000000..574cf16 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/sauce-labs.svg @@ -0,0 +1,81 @@ + + + + + Sauce Labs + + + + + + + + + + + + + + + + diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/util/clear.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/util/clear.js new file mode 100644 index 0000000..48826bf --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/util/clear.js @@ -0,0 +1,35 @@ +'use strict' + +module.exports = function clear (db, location, keyRange, options, callback) { + if (options.limit === 0) return db.nextTick(callback) + + const transaction = db.db.transaction([location], 'readwrite') + const store = transaction.objectStore(location) + let count = 0 + + transaction.oncomplete = function () { + callback() + } + + transaction.onabort = function () { + callback(transaction.error || new Error('aborted by user')) + } + + // A key cursor is faster (skips reading values) but not supported by IE + // TODO: we no longer support IE. Test others + const method = store.openKeyCursor ? 'openKeyCursor' : 'openCursor' + const direction = options.reverse ? 'prev' : 'next' + + store[method](keyRange, direction).onsuccess = function (ev) { + const cursor = ev.target.result + + if (cursor) { + // Wait for a request to complete before continuing, saving CPU. + store.delete(cursor.key).onsuccess = function () { + if (options.limit <= 0 || ++count < options.limit) { + cursor.continue() + } + } + } + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/util/deserialize.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/util/deserialize.js new file mode 100644 index 0000000..41dabee --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/util/deserialize.js @@ -0,0 +1,14 @@ +'use strict' + +const textEncoder = new TextEncoder() + +module.exports = function (data) { + if (data instanceof Uint8Array) { + return data + } else if (data instanceof ArrayBuffer) { + return new Uint8Array(data) + } else { + // Non-binary data stored with an old version (level-js < 5.0.0) + return textEncoder.encode(data) + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/util/key-range.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/util/key-range.js new file mode 100644 index 0000000..761da15 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/browser-level/util/key-range.js @@ -0,0 +1,20 @@ +/* global IDBKeyRange */ + +'use strict' + +module.exports = function createKeyRange (options) { + const lower = options.gte !== undefined ? options.gte : options.gt !== undefined ? options.gt : undefined + const upper = options.lte !== undefined ? options.lte : options.lt !== undefined ? options.lt : undefined + const lowerExclusive = options.gte === undefined + const upperExclusive = options.lte === undefined + + if (lower !== undefined && upper !== undefined) { + return IDBKeyRange.bound(lower, upper, lowerExclusive, upperExclusive) + } else if (lower !== undefined) { + return IDBKeyRange.lowerBound(lower, lowerExclusive) + } else if (upper !== undefined) { + return IDBKeyRange.upperBound(upper, upperExclusive) + } else { + return null + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/AUTHORS.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/AUTHORS.md new file mode 100644 index 0000000..468aa19 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/AUTHORS.md @@ -0,0 +1,73 @@ +# Authors + +#### Ordered by first contribution. + +- Romain Beauxis (toots@rastageeks.org) +- Tobias Koppers (tobias.koppers@googlemail.com) +- Janus (ysangkok@gmail.com) +- Rainer Dreyer (rdrey1@gmail.com) +- Tõnis Tiigi (tonistiigi@gmail.com) +- James Halliday (mail@substack.net) +- Michael Williamson (mike@zwobble.org) +- elliottcable (github@elliottcable.name) +- rafael (rvalle@livelens.net) +- Andrew Kelley (superjoe30@gmail.com) +- Andreas Madsen (amwebdk@gmail.com) +- Mike Brevoort (mike.brevoort@pearson.com) +- Brian White (mscdex@mscdex.net) +- Feross Aboukhadijeh (feross@feross.org) +- Ruben Verborgh (ruben@verborgh.org) +- eliang (eliang.cs@gmail.com) +- Jesse Tane (jesse.tane@gmail.com) +- Alfonso Boza (alfonso@cloud.com) +- Mathias Buus (mathiasbuus@gmail.com) +- Devon Govett (devongovett@gmail.com) +- Daniel Cousens (github@dcousens.com) +- Joseph Dykstra (josephdykstra@gmail.com) +- Parsha Pourkhomami (parshap+git@gmail.com) +- Damjan Košir (damjan.kosir@gmail.com) +- daverayment (dave.rayment@gmail.com) +- kawanet (u-suke@kawa.net) +- Linus Unnebäck (linus@folkdatorn.se) +- Nolan Lawson (nolan.lawson@gmail.com) +- Calvin Metcalf (calvin.metcalf@gmail.com) +- Koki Takahashi (hakatasiloving@gmail.com) +- Guy Bedford (guybedford@gmail.com) +- Jan Schär (jscissr@gmail.com) +- RaulTsc (tomescu.raul@gmail.com) +- Matthieu Monsch (monsch@alum.mit.edu) +- Dan Ehrenberg (littledan@chromium.org) +- Kirill Fomichev (fanatid@ya.ru) +- Yusuke Kawasaki (u-suke@kawa.net) +- DC (dcposch@dcpos.ch) +- John-David Dalton (john.david.dalton@gmail.com) +- adventure-yunfei (adventure030@gmail.com) +- Emil Bay (github@tixz.dk) +- Sam Sudar (sudar.sam@gmail.com) +- Volker Mische (volker.mische@gmail.com) +- David Walton (support@geekstocks.com) +- Сковорода Никита Андреевич (chalkerx@gmail.com) +- greenkeeper[bot] (greenkeeper[bot]@users.noreply.github.com) +- ukstv (sergey.ukustov@machinomy.com) +- Renée Kooi (renee@kooi.me) +- ranbochen (ranbochen@qq.com) +- Vladimir Borovik (bobahbdb@gmail.com) +- greenkeeper[bot] (23040076+greenkeeper[bot]@users.noreply.github.com) +- kumavis (aaron@kumavis.me) +- Sergey Ukustov (sergey.ukustov@machinomy.com) +- Fei Liu (liu.feiwood@gmail.com) +- Blaine Bublitz (blaine.bublitz@gmail.com) +- clement (clement@seald.io) +- Koushik Dutta (koushd@gmail.com) +- Jordan Harband (ljharb@gmail.com) +- Niklas Mischkulnig (mischnic@users.noreply.github.com) +- Nikolai Vavilov (vvnicholas@gmail.com) +- Fedor Nezhivoi (gyzerok@users.noreply.github.com) +- shuse2 (shus.toda@gmail.com) +- Peter Newman (peternewman@users.noreply.github.com) +- mathmakgakpak (44949126+mathmakgakpak@users.noreply.github.com) +- jkkang (jkkang@smartauth.kr) +- Deklan Webster (deklanw@gmail.com) +- Martin Heidegger (martin.heidegger@gmail.com) + +#### Generated by bin/update-authors.sh. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/LICENSE new file mode 100644 index 0000000..d6bf75d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Feross Aboukhadijeh, and other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/README.md new file mode 100644 index 0000000..451e235 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/README.md @@ -0,0 +1,410 @@ +# buffer [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url] + +[travis-image]: https://img.shields.io/travis/feross/buffer/master.svg +[travis-url]: https://travis-ci.org/feross/buffer +[npm-image]: https://img.shields.io/npm/v/buffer.svg +[npm-url]: https://npmjs.org/package/buffer +[downloads-image]: https://img.shields.io/npm/dm/buffer.svg +[downloads-url]: https://npmjs.org/package/buffer +[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg +[standard-url]: https://standardjs.com + +#### The buffer module from [node.js](https://nodejs.org/), for the browser. + +[![saucelabs][saucelabs-image]][saucelabs-url] + +[saucelabs-image]: https://saucelabs.com/browser-matrix/buffer.svg +[saucelabs-url]: https://saucelabs.com/u/buffer + +With [browserify](http://browserify.org), simply `require('buffer')` or use the `Buffer` global and you will get this module. + +The goal is to provide an API that is 100% identical to +[node's Buffer API](https://nodejs.org/api/buffer.html). Read the +[official docs](https://nodejs.org/api/buffer.html) for the full list of properties, +instance methods, and class methods that are supported. + +## features + +- Manipulate binary data like a boss, in all browsers! +- Super fast. Backed by Typed Arrays (`Uint8Array`/`ArrayBuffer`, not `Object`) +- Extremely small bundle size (**6.75KB minified + gzipped**, 51.9KB with comments) +- Excellent browser support (Chrome, Firefox, Edge, Safari 11+, iOS 11+, Android, etc.) +- Preserves Node API exactly, with one minor difference (see below) +- Square-bracket `buf[4]` notation works! +- Does not modify any browser prototypes or put anything on `window` +- Comprehensive test suite (including all buffer tests from node.js core) + +## install + +To use this module directly (without browserify), install it: + +```bash +npm install buffer +``` + +This module was previously called **native-buffer-browserify**, but please use **buffer** +from now on. + +If you do not use a bundler, you can use the [standalone script](https://bundle.run/buffer). + +## usage + +The module's API is identical to node's `Buffer` API. Read the +[official docs](https://nodejs.org/api/buffer.html) for the full list of properties, +instance methods, and class methods that are supported. + +As mentioned above, `require('buffer')` or use the `Buffer` global with +[browserify](http://browserify.org) and this module will automatically be included +in your bundle. Almost any npm module will work in the browser, even if it assumes that +the node `Buffer` API will be available. + +To depend on this module explicitly (without browserify), require it like this: + +```js +var Buffer = require('buffer/').Buffer // note: the trailing slash is important! +``` + +To require this module explicitly, use `require('buffer/')` which tells the node.js module +lookup algorithm (also used by browserify) to use the **npm module** named `buffer` +instead of the **node.js core** module named `buffer`! + + +## how does it work? + +The Buffer constructor returns instances of `Uint8Array` that have their prototype +changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of `Uint8Array`, +so the returned instances will have all the node `Buffer` methods and the +`Uint8Array` methods. Square bracket notation works as expected -- it returns a +single octet. + +The `Uint8Array` prototype remains unmodified. + + +## tracking the latest node api + +This module tracks the Buffer API in the latest (unstable) version of node.js. The Buffer +API is considered **stable** in the +[node stability index](https://nodejs.org/docs/latest/api/documentation.html#documentation_stability_index), +so it is unlikely that there will ever be breaking changes. +Nonetheless, when/if the Buffer API changes in node, this module's API will change +accordingly. + +## related packages + +- [`buffer-reverse`](https://www.npmjs.com/package/buffer-reverse) - Reverse a buffer +- [`buffer-xor`](https://www.npmjs.com/package/buffer-xor) - Bitwise xor a buffer +- [`is-buffer`](https://www.npmjs.com/package/is-buffer) - Determine if an object is a Buffer without including the whole `Buffer` package + +## conversion packages + +### convert typed array to buffer + +Use [`typedarray-to-buffer`](https://www.npmjs.com/package/typedarray-to-buffer) to convert any kind of typed array to a `Buffer`. Does not perform a copy, so it's super fast. + +### convert buffer to typed array + +`Buffer` is a subclass of `Uint8Array` (which is a typed array). So there is no need to explicitly convert to typed array. Just use the buffer as a `Uint8Array`. + +### convert blob to buffer + +Use [`blob-to-buffer`](https://www.npmjs.com/package/blob-to-buffer) to convert a `Blob` to a `Buffer`. + +### convert buffer to blob + +To convert a `Buffer` to a `Blob`, use the `Blob` constructor: + +```js +var blob = new Blob([ buffer ]) +``` + +Optionally, specify a mimetype: + +```js +var blob = new Blob([ buffer ], { type: 'text/html' }) +``` + +### convert arraybuffer to buffer + +To convert an `ArrayBuffer` to a `Buffer`, use the `Buffer.from` function. Does not perform a copy, so it's super fast. + +```js +var buffer = Buffer.from(arrayBuffer) +``` + +### convert buffer to arraybuffer + +To convert a `Buffer` to an `ArrayBuffer`, use the `.buffer` property (which is present on all `Uint8Array` objects): + +```js +var arrayBuffer = buffer.buffer.slice( + buffer.byteOffset, buffer.byteOffset + buffer.byteLength +) +``` + +Alternatively, use the [`to-arraybuffer`](https://www.npmjs.com/package/to-arraybuffer) module. + +## performance + +See perf tests in `/perf`. + +`BrowserBuffer` is the browser `buffer` module (this repo). `Uint8Array` is included as a +sanity check (since `BrowserBuffer` uses `Uint8Array` under the hood, `Uint8Array` will +always be at least a bit faster). Finally, `NodeBuffer` is the node.js buffer module, +which is included to compare against. + +NOTE: Performance has improved since these benchmarks were taken. PR welcome to update the README. + +### Chrome 38 + +| Method | Operations | Accuracy | Sampled | Fastest | +|:-------|:-----------|:---------|:--------|:-------:| +| BrowserBuffer#bracket-notation | 11,457,464 ops/sec | ±0.86% | 66 | ✓ | +| Uint8Array#bracket-notation | 10,824,332 ops/sec | ±0.74% | 65 | | +| | | | | +| BrowserBuffer#concat | 450,532 ops/sec | ±0.76% | 68 | | +| Uint8Array#concat | 1,368,911 ops/sec | ±1.50% | 62 | ✓ | +| | | | | +| BrowserBuffer#copy(16000) | 903,001 ops/sec | ±0.96% | 67 | | +| Uint8Array#copy(16000) | 1,422,441 ops/sec | ±1.04% | 66 | ✓ | +| | | | | +| BrowserBuffer#copy(16) | 11,431,358 ops/sec | ±0.46% | 69 | | +| Uint8Array#copy(16) | 13,944,163 ops/sec | ±1.12% | 68 | ✓ | +| | | | | +| BrowserBuffer#new(16000) | 106,329 ops/sec | ±6.70% | 44 | | +| Uint8Array#new(16000) | 131,001 ops/sec | ±2.85% | 31 | ✓ | +| | | | | +| BrowserBuffer#new(16) | 1,554,491 ops/sec | ±1.60% | 65 | | +| Uint8Array#new(16) | 6,623,930 ops/sec | ±1.66% | 65 | ✓ | +| | | | | +| BrowserBuffer#readDoubleBE | 112,830 ops/sec | ±0.51% | 69 | ✓ | +| DataView#getFloat64 | 93,500 ops/sec | ±0.57% | 68 | | +| | | | | +| BrowserBuffer#readFloatBE | 146,678 ops/sec | ±0.95% | 68 | ✓ | +| DataView#getFloat32 | 99,311 ops/sec | ±0.41% | 67 | | +| | | | | +| BrowserBuffer#readUInt32LE | 843,214 ops/sec | ±0.70% | 69 | ✓ | +| DataView#getUint32 | 103,024 ops/sec | ±0.64% | 67 | | +| | | | | +| BrowserBuffer#slice | 1,013,941 ops/sec | ±0.75% | 67 | | +| Uint8Array#subarray | 1,903,928 ops/sec | ±0.53% | 67 | ✓ | +| | | | | +| BrowserBuffer#writeFloatBE | 61,387 ops/sec | ±0.90% | 67 | | +| DataView#setFloat32 | 141,249 ops/sec | ±0.40% | 66 | ✓ | + + +### Firefox 33 + +| Method | Operations | Accuracy | Sampled | Fastest | +|:-------|:-----------|:---------|:--------|:-------:| +| BrowserBuffer#bracket-notation | 20,800,421 ops/sec | ±1.84% | 60 | | +| Uint8Array#bracket-notation | 20,826,235 ops/sec | ±2.02% | 61 | ✓ | +| | | | | +| BrowserBuffer#concat | 153,076 ops/sec | ±2.32% | 61 | | +| Uint8Array#concat | 1,255,674 ops/sec | ±8.65% | 52 | ✓ | +| | | | | +| BrowserBuffer#copy(16000) | 1,105,312 ops/sec | ±1.16% | 63 | | +| Uint8Array#copy(16000) | 1,615,911 ops/sec | ±0.55% | 66 | ✓ | +| | | | | +| BrowserBuffer#copy(16) | 16,357,599 ops/sec | ±0.73% | 68 | | +| Uint8Array#copy(16) | 31,436,281 ops/sec | ±1.05% | 68 | ✓ | +| | | | | +| BrowserBuffer#new(16000) | 52,995 ops/sec | ±6.01% | 35 | | +| Uint8Array#new(16000) | 87,686 ops/sec | ±5.68% | 45 | ✓ | +| | | | | +| BrowserBuffer#new(16) | 252,031 ops/sec | ±1.61% | 66 | | +| Uint8Array#new(16) | 8,477,026 ops/sec | ±0.49% | 68 | ✓ | +| | | | | +| BrowserBuffer#readDoubleBE | 99,871 ops/sec | ±0.41% | 69 | | +| DataView#getFloat64 | 285,663 ops/sec | ±0.70% | 68 | ✓ | +| | | | | +| BrowserBuffer#readFloatBE | 115,540 ops/sec | ±0.42% | 69 | | +| DataView#getFloat32 | 288,722 ops/sec | ±0.82% | 68 | ✓ | +| | | | | +| BrowserBuffer#readUInt32LE | 633,926 ops/sec | ±1.08% | 67 | ✓ | +| DataView#getUint32 | 294,808 ops/sec | ±0.79% | 64 | | +| | | | | +| BrowserBuffer#slice | 349,425 ops/sec | ±0.46% | 69 | | +| Uint8Array#subarray | 5,965,819 ops/sec | ±0.60% | 65 | ✓ | +| | | | | +| BrowserBuffer#writeFloatBE | 59,980 ops/sec | ±0.41% | 67 | | +| DataView#setFloat32 | 317,634 ops/sec | ±0.63% | 68 | ✓ | + +### Safari 8 + +| Method | Operations | Accuracy | Sampled | Fastest | +|:-------|:-----------|:---------|:--------|:-------:| +| BrowserBuffer#bracket-notation | 10,279,729 ops/sec | ±2.25% | 56 | ✓ | +| Uint8Array#bracket-notation | 10,030,767 ops/sec | ±2.23% | 59 | | +| | | | | +| BrowserBuffer#concat | 144,138 ops/sec | ±1.38% | 65 | | +| Uint8Array#concat | 4,950,764 ops/sec | ±1.70% | 63 | ✓ | +| | | | | +| BrowserBuffer#copy(16000) | 1,058,548 ops/sec | ±1.51% | 64 | | +| Uint8Array#copy(16000) | 1,409,666 ops/sec | ±1.17% | 65 | ✓ | +| | | | | +| BrowserBuffer#copy(16) | 6,282,529 ops/sec | ±1.88% | 58 | | +| Uint8Array#copy(16) | 11,907,128 ops/sec | ±2.87% | 58 | ✓ | +| | | | | +| BrowserBuffer#new(16000) | 101,663 ops/sec | ±3.89% | 57 | | +| Uint8Array#new(16000) | 22,050,818 ops/sec | ±6.51% | 46 | ✓ | +| | | | | +| BrowserBuffer#new(16) | 176,072 ops/sec | ±2.13% | 64 | | +| Uint8Array#new(16) | 24,385,731 ops/sec | ±5.01% | 51 | ✓ | +| | | | | +| BrowserBuffer#readDoubleBE | 41,341 ops/sec | ±1.06% | 67 | | +| DataView#getFloat64 | 322,280 ops/sec | ±0.84% | 68 | ✓ | +| | | | | +| BrowserBuffer#readFloatBE | 46,141 ops/sec | ±1.06% | 65 | | +| DataView#getFloat32 | 337,025 ops/sec | ±0.43% | 69 | ✓ | +| | | | | +| BrowserBuffer#readUInt32LE | 151,551 ops/sec | ±1.02% | 66 | | +| DataView#getUint32 | 308,278 ops/sec | ±0.94% | 67 | ✓ | +| | | | | +| BrowserBuffer#slice | 197,365 ops/sec | ±0.95% | 66 | | +| Uint8Array#subarray | 9,558,024 ops/sec | ±3.08% | 58 | ✓ | +| | | | | +| BrowserBuffer#writeFloatBE | 17,518 ops/sec | ±1.03% | 63 | | +| DataView#setFloat32 | 319,751 ops/sec | ±0.48% | 68 | ✓ | + + +### Node 0.11.14 + +| Method | Operations | Accuracy | Sampled | Fastest | +|:-------|:-----------|:---------|:--------|:-------:| +| BrowserBuffer#bracket-notation | 10,489,828 ops/sec | ±3.25% | 90 | | +| Uint8Array#bracket-notation | 10,534,884 ops/sec | ±0.81% | 92 | ✓ | +| NodeBuffer#bracket-notation | 10,389,910 ops/sec | ±0.97% | 87 | | +| | | | | +| BrowserBuffer#concat | 487,830 ops/sec | ±2.58% | 88 | | +| Uint8Array#concat | 1,814,327 ops/sec | ±1.28% | 88 | ✓ | +| NodeBuffer#concat | 1,636,523 ops/sec | ±1.88% | 73 | | +| | | | | +| BrowserBuffer#copy(16000) | 1,073,665 ops/sec | ±0.77% | 90 | | +| Uint8Array#copy(16000) | 1,348,517 ops/sec | ±0.84% | 89 | ✓ | +| NodeBuffer#copy(16000) | 1,289,533 ops/sec | ±0.82% | 93 | | +| | | | | +| BrowserBuffer#copy(16) | 12,782,706 ops/sec | ±0.74% | 85 | | +| Uint8Array#copy(16) | 14,180,427 ops/sec | ±0.93% | 92 | ✓ | +| NodeBuffer#copy(16) | 11,083,134 ops/sec | ±1.06% | 89 | | +| | | | | +| BrowserBuffer#new(16000) | 141,678 ops/sec | ±3.30% | 67 | | +| Uint8Array#new(16000) | 161,491 ops/sec | ±2.96% | 60 | | +| NodeBuffer#new(16000) | 292,699 ops/sec | ±3.20% | 55 | ✓ | +| | | | | +| BrowserBuffer#new(16) | 1,655,466 ops/sec | ±2.41% | 82 | | +| Uint8Array#new(16) | 14,399,926 ops/sec | ±0.91% | 94 | ✓ | +| NodeBuffer#new(16) | 3,894,696 ops/sec | ±0.88% | 92 | | +| | | | | +| BrowserBuffer#readDoubleBE | 109,582 ops/sec | ±0.75% | 93 | ✓ | +| DataView#getFloat64 | 91,235 ops/sec | ±0.81% | 90 | | +| NodeBuffer#readDoubleBE | 88,593 ops/sec | ±0.96% | 81 | | +| | | | | +| BrowserBuffer#readFloatBE | 139,854 ops/sec | ±1.03% | 85 | ✓ | +| DataView#getFloat32 | 98,744 ops/sec | ±0.80% | 89 | | +| NodeBuffer#readFloatBE | 92,769 ops/sec | ±0.94% | 93 | | +| | | | | +| BrowserBuffer#readUInt32LE | 710,861 ops/sec | ±0.82% | 92 | | +| DataView#getUint32 | 117,893 ops/sec | ±0.84% | 91 | | +| NodeBuffer#readUInt32LE | 851,412 ops/sec | ±0.72% | 93 | ✓ | +| | | | | +| BrowserBuffer#slice | 1,673,877 ops/sec | ±0.73% | 94 | | +| Uint8Array#subarray | 6,919,243 ops/sec | ±0.67% | 90 | ✓ | +| NodeBuffer#slice | 4,617,604 ops/sec | ±0.79% | 93 | | +| | | | | +| BrowserBuffer#writeFloatBE | 66,011 ops/sec | ±0.75% | 93 | | +| DataView#setFloat32 | 127,760 ops/sec | ±0.72% | 93 | ✓ | +| NodeBuffer#writeFloatBE | 103,352 ops/sec | ±0.83% | 93 | | + +### iojs 1.8.1 + +| Method | Operations | Accuracy | Sampled | Fastest | +|:-------|:-----------|:---------|:--------|:-------:| +| BrowserBuffer#bracket-notation | 10,990,488 ops/sec | ±1.11% | 91 | | +| Uint8Array#bracket-notation | 11,268,757 ops/sec | ±0.65% | 97 | | +| NodeBuffer#bracket-notation | 11,353,260 ops/sec | ±0.83% | 94 | ✓ | +| | | | | +| BrowserBuffer#concat | 378,954 ops/sec | ±0.74% | 94 | | +| Uint8Array#concat | 1,358,288 ops/sec | ±0.97% | 87 | | +| NodeBuffer#concat | 1,934,050 ops/sec | ±1.11% | 78 | ✓ | +| | | | | +| BrowserBuffer#copy(16000) | 894,538 ops/sec | ±0.56% | 84 | | +| Uint8Array#copy(16000) | 1,442,656 ops/sec | ±0.71% | 96 | | +| NodeBuffer#copy(16000) | 1,457,898 ops/sec | ±0.53% | 92 | ✓ | +| | | | | +| BrowserBuffer#copy(16) | 12,870,457 ops/sec | ±0.67% | 95 | | +| Uint8Array#copy(16) | 16,643,989 ops/sec | ±0.61% | 93 | ✓ | +| NodeBuffer#copy(16) | 14,885,848 ops/sec | ±0.74% | 94 | | +| | | | | +| BrowserBuffer#new(16000) | 109,264 ops/sec | ±4.21% | 63 | | +| Uint8Array#new(16000) | 138,916 ops/sec | ±1.87% | 61 | | +| NodeBuffer#new(16000) | 281,449 ops/sec | ±3.58% | 51 | ✓ | +| | | | | +| BrowserBuffer#new(16) | 1,362,935 ops/sec | ±0.56% | 99 | | +| Uint8Array#new(16) | 6,193,090 ops/sec | ±0.64% | 95 | ✓ | +| NodeBuffer#new(16) | 4,745,425 ops/sec | ±1.56% | 90 | | +| | | | | +| BrowserBuffer#readDoubleBE | 118,127 ops/sec | ±0.59% | 93 | ✓ | +| DataView#getFloat64 | 107,332 ops/sec | ±0.65% | 91 | | +| NodeBuffer#readDoubleBE | 116,274 ops/sec | ±0.94% | 95 | | +| | | | | +| BrowserBuffer#readFloatBE | 150,326 ops/sec | ±0.58% | 95 | ✓ | +| DataView#getFloat32 | 110,541 ops/sec | ±0.57% | 98 | | +| NodeBuffer#readFloatBE | 121,599 ops/sec | ±0.60% | 87 | | +| | | | | +| BrowserBuffer#readUInt32LE | 814,147 ops/sec | ±0.62% | 93 | | +| DataView#getUint32 | 137,592 ops/sec | ±0.64% | 90 | | +| NodeBuffer#readUInt32LE | 931,650 ops/sec | ±0.71% | 96 | ✓ | +| | | | | +| BrowserBuffer#slice | 878,590 ops/sec | ±0.68% | 93 | | +| Uint8Array#subarray | 2,843,308 ops/sec | ±1.02% | 90 | | +| NodeBuffer#slice | 4,998,316 ops/sec | ±0.68% | 90 | ✓ | +| | | | | +| BrowserBuffer#writeFloatBE | 65,927 ops/sec | ±0.74% | 93 | | +| DataView#setFloat32 | 139,823 ops/sec | ±0.97% | 89 | ✓ | +| NodeBuffer#writeFloatBE | 135,763 ops/sec | ±0.65% | 96 | | +| | | | | + +## Testing the project + +First, install the project: + + npm install + +Then, to run tests in Node.js, run: + + npm run test-node + +To test locally in a browser, you can run: + + npm run test-browser-es5-local # For ES5 browsers that don't support ES6 + npm run test-browser-es6-local # For ES6 compliant browsers + +This will print out a URL that you can then open in a browser to run the tests, using [airtap](https://www.npmjs.com/package/airtap). + +To run automated browser tests using Saucelabs, ensure that your `SAUCE_USERNAME` and `SAUCE_ACCESS_KEY` environment variables are set, then run: + + npm test + +This is what's run in Travis, to check against various browsers. The list of browsers is kept in the `bin/airtap-es5.yml` and `bin/airtap-es6.yml` files. + +## JavaScript Standard Style + +This module uses [JavaScript Standard Style](https://github.com/feross/standard). + +[![JavaScript Style Guide](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard) + +To test that the code conforms to the style, `npm install` and run: + + ./node_modules/.bin/standard + +## credit + +This was originally forked from [buffer-browserify](https://github.com/toots/buffer-browserify). + +## Security Policies and Procedures + +The `buffer` team and community take all security bugs in `buffer` seriously. Please see our [security policies and procedures](https://github.com/feross/security) document to learn how to report issues. + +## license + +MIT. Copyright (C) [Feross Aboukhadijeh](http://feross.org), and other contributors. Originally forked from an MIT-licensed module by Romain Beauxis. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/index.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/index.d.ts new file mode 100644 index 0000000..07096a2 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/index.d.ts @@ -0,0 +1,194 @@ +export class Buffer extends Uint8Array { + length: number + write(string: string, offset?: number, length?: number, encoding?: string): number; + toString(encoding?: string, start?: number, end?: number): string; + toJSON(): { type: 'Buffer', data: any[] }; + equals(otherBuffer: Buffer): boolean; + compare(otherBuffer: Uint8Array, targetStart?: number, targetEnd?: number, sourceStart?: number, sourceEnd?: number): number; + copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number; + slice(start?: number, end?: number): Buffer; + writeUIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeUIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + readUIntLE(offset: number, byteLength: number, noAssert?: boolean): number; + readUIntBE(offset: number, byteLength: number, noAssert?: boolean): number; + readIntLE(offset: number, byteLength: number, noAssert?: boolean): number; + readIntBE(offset: number, byteLength: number, noAssert?: boolean): number; + readUInt8(offset: number, noAssert?: boolean): number; + readUInt16LE(offset: number, noAssert?: boolean): number; + readUInt16BE(offset: number, noAssert?: boolean): number; + readUInt32LE(offset: number, noAssert?: boolean): number; + readUInt32BE(offset: number, noAssert?: boolean): number; + readBigUInt64LE(offset: number): BigInt; + readBigUInt64BE(offset: number): BigInt; + readInt8(offset: number, noAssert?: boolean): number; + readInt16LE(offset: number, noAssert?: boolean): number; + readInt16BE(offset: number, noAssert?: boolean): number; + readInt32LE(offset: number, noAssert?: boolean): number; + readInt32BE(offset: number, noAssert?: boolean): number; + readBigInt64LE(offset: number): BigInt; + readBigInt64BE(offset: number): BigInt; + readFloatLE(offset: number, noAssert?: boolean): number; + readFloatBE(offset: number, noAssert?: boolean): number; + readDoubleLE(offset: number, noAssert?: boolean): number; + readDoubleBE(offset: number, noAssert?: boolean): number; + reverse(): this; + swap16(): Buffer; + swap32(): Buffer; + swap64(): Buffer; + writeUInt8(value: number, offset: number, noAssert?: boolean): number; + writeUInt16LE(value: number, offset: number, noAssert?: boolean): number; + writeUInt16BE(value: number, offset: number, noAssert?: boolean): number; + writeUInt32LE(value: number, offset: number, noAssert?: boolean): number; + writeUInt32BE(value: number, offset: number, noAssert?: boolean): number; + writeBigUInt64LE(value: number, offset: number): BigInt; + writeBigUInt64BE(value: number, offset: number): BigInt; + writeInt8(value: number, offset: number, noAssert?: boolean): number; + writeInt16LE(value: number, offset: number, noAssert?: boolean): number; + writeInt16BE(value: number, offset: number, noAssert?: boolean): number; + writeInt32LE(value: number, offset: number, noAssert?: boolean): number; + writeInt32BE(value: number, offset: number, noAssert?: boolean): number; + writeBigInt64LE(value: number, offset: number): BigInt; + writeBigInt64BE(value: number, offset: number): BigInt; + writeFloatLE(value: number, offset: number, noAssert?: boolean): number; + writeFloatBE(value: number, offset: number, noAssert?: boolean): number; + writeDoubleLE(value: number, offset: number, noAssert?: boolean): number; + writeDoubleBE(value: number, offset: number, noAssert?: boolean): number; + fill(value: any, offset?: number, end?: number): this; + indexOf(value: string | number | Buffer, byteOffset?: number, encoding?: string): number; + lastIndexOf(value: string | number | Buffer, byteOffset?: number, encoding?: string): number; + includes(value: string | number | Buffer, byteOffset?: number, encoding?: string): boolean; + + /** + * Allocates a new buffer containing the given {str}. + * + * @param str String to store in buffer. + * @param encoding encoding to use, optional. Default is 'utf8' + */ + constructor (str: string, encoding?: string); + /** + * Allocates a new buffer of {size} octets. + * + * @param size count of octets to allocate. + */ + constructor (size: number); + /** + * Allocates a new buffer containing the given {array} of octets. + * + * @param array The octets to store. + */ + constructor (array: Uint8Array); + /** + * Produces a Buffer backed by the same allocated memory as + * the given {ArrayBuffer}. + * + * + * @param arrayBuffer The ArrayBuffer with which to share memory. + */ + constructor (arrayBuffer: ArrayBuffer); + /** + * Allocates a new buffer containing the given {array} of octets. + * + * @param array The octets to store. + */ + constructor (array: any[]); + /** + * Copies the passed {buffer} data onto a new {Buffer} instance. + * + * @param buffer The buffer to copy. + */ + constructor (buffer: Buffer); + prototype: Buffer; + /** + * Allocates a new Buffer using an {array} of octets. + * + * @param array + */ + static from(array: any[]): Buffer; + /** + * When passed a reference to the .buffer property of a TypedArray instance, + * the newly created Buffer will share the same allocated memory as the TypedArray. + * The optional {byteOffset} and {length} arguments specify a memory range + * within the {arrayBuffer} that will be shared by the Buffer. + * + * @param arrayBuffer The .buffer property of a TypedArray or a new ArrayBuffer() + * @param byteOffset + * @param length + */ + static from(arrayBuffer: ArrayBuffer, byteOffset?: number, length?: number): Buffer; + /** + * Copies the passed {buffer} data onto a new Buffer instance. + * + * @param buffer + */ + static from(buffer: Buffer | Uint8Array): Buffer; + /** + * Creates a new Buffer containing the given JavaScript string {str}. + * If provided, the {encoding} parameter identifies the character encoding. + * If not provided, {encoding} defaults to 'utf8'. + * + * @param str + */ + static from(str: string, encoding?: string): Buffer; + /** + * Returns true if {obj} is a Buffer + * + * @param obj object to test. + */ + static isBuffer(obj: any): obj is Buffer; + /** + * Returns true if {encoding} is a valid encoding argument. + * Valid string encodings in Node 0.12: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex' + * + * @param encoding string to test. + */ + static isEncoding(encoding: string): boolean; + /** + * Gives the actual byte length of a string. encoding defaults to 'utf8'. + * This is not the same as String.prototype.length since that returns the number of characters in a string. + * + * @param string string to test. + * @param encoding encoding used to evaluate (defaults to 'utf8') + */ + static byteLength(string: string, encoding?: string): number; + /** + * Returns a buffer which is the result of concatenating all the buffers in the list together. + * + * If the list has no items, or if the totalLength is 0, then it returns a zero-length buffer. + * If the list has exactly one item, then the first item of the list is returned. + * If the list has more than one item, then a new Buffer is created. + * + * @param list An array of Buffer objects to concatenate + * @param totalLength Total length of the buffers when concatenated. + * If totalLength is not provided, it is read from the buffers in the list. However, this adds an additional loop to the function, so it is faster to provide the length explicitly. + */ + static concat(list: Uint8Array[], totalLength?: number): Buffer; + /** + * The same as buf1.compare(buf2). + */ + static compare(buf1: Uint8Array, buf2: Uint8Array): number; + /** + * Allocates a new buffer of {size} octets. + * + * @param size count of octets to allocate. + * @param fill if specified, buffer will be initialized by calling buf.fill(fill). + * If parameter is omitted, buffer will be filled with zeros. + * @param encoding encoding used for call to buf.fill while initializing + */ + static alloc(size: number, fill?: string | Buffer | number, encoding?: string): Buffer; + /** + * Allocates a new buffer of {size} octets, leaving memory not initialized, so the contents + * of the newly created Buffer are unknown and may contain sensitive data. + * + * @param size count of octets to allocate + */ + static allocUnsafe(size: number): Buffer; + /** + * Allocates a new non-pooled buffer of {size} octets, leaving memory not initialized, so the contents + * of the newly created Buffer are unknown and may contain sensitive data. + * + * @param size count of octets to allocate + */ + static allocUnsafeSlow(size: number): Buffer; +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/index.js new file mode 100644 index 0000000..7a0e9c2 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/index.js @@ -0,0 +1,2106 @@ +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +/* eslint-disable no-proto */ + +'use strict' + +const base64 = require('base64-js') +const ieee754 = require('ieee754') +const customInspectSymbol = + (typeof Symbol === 'function' && typeof Symbol['for'] === 'function') // eslint-disable-line dot-notation + ? Symbol['for']('nodejs.util.inspect.custom') // eslint-disable-line dot-notation + : null + +exports.Buffer = Buffer +exports.SlowBuffer = SlowBuffer +exports.INSPECT_MAX_BYTES = 50 + +const K_MAX_LENGTH = 0x7fffffff +exports.kMaxLength = K_MAX_LENGTH + +/** + * If `Buffer.TYPED_ARRAY_SUPPORT`: + * === true Use Uint8Array implementation (fastest) + * === false Print warning and recommend using `buffer` v4.x which has an Object + * implementation (most compatible, even IE6) + * + * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, + * Opera 11.6+, iOS 4.2+. + * + * We report that the browser does not support typed arrays if the are not subclassable + * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array` + * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support + * for __proto__ and has a buggy typed array implementation. + */ +Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport() + +if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && + typeof console.error === 'function') { + console.error( + 'This browser lacks typed array (Uint8Array) support which is required by ' + + '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.' + ) +} + +function typedArraySupport () { + // Can typed array instances can be augmented? + try { + const arr = new Uint8Array(1) + const proto = { foo: function () { return 42 } } + Object.setPrototypeOf(proto, Uint8Array.prototype) + Object.setPrototypeOf(arr, proto) + return arr.foo() === 42 + } catch (e) { + return false + } +} + +Object.defineProperty(Buffer.prototype, 'parent', { + enumerable: true, + get: function () { + if (!Buffer.isBuffer(this)) return undefined + return this.buffer + } +}) + +Object.defineProperty(Buffer.prototype, 'offset', { + enumerable: true, + get: function () { + if (!Buffer.isBuffer(this)) return undefined + return this.byteOffset + } +}) + +function createBuffer (length) { + if (length > K_MAX_LENGTH) { + throw new RangeError('The value "' + length + '" is invalid for option "size"') + } + // Return an augmented `Uint8Array` instance + const buf = new Uint8Array(length) + Object.setPrototypeOf(buf, Buffer.prototype) + return buf +} + +/** + * The Buffer constructor returns instances of `Uint8Array` that have their + * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of + * `Uint8Array`, so the returned instances will have all the node `Buffer` methods + * and the `Uint8Array` methods. Square bracket notation works as expected -- it + * returns a single octet. + * + * The `Uint8Array` prototype remains unmodified. + */ + +function Buffer (arg, encodingOrOffset, length) { + // Common case. + if (typeof arg === 'number') { + if (typeof encodingOrOffset === 'string') { + throw new TypeError( + 'The "string" argument must be of type string. Received type number' + ) + } + return allocUnsafe(arg) + } + return from(arg, encodingOrOffset, length) +} + +Buffer.poolSize = 8192 // not used by this implementation + +function from (value, encodingOrOffset, length) { + if (typeof value === 'string') { + return fromString(value, encodingOrOffset) + } + + if (ArrayBuffer.isView(value)) { + return fromArrayView(value) + } + + if (value == null) { + throw new TypeError( + 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + + 'or Array-like Object. Received type ' + (typeof value) + ) + } + + if (isInstance(value, ArrayBuffer) || + (value && isInstance(value.buffer, ArrayBuffer))) { + return fromArrayBuffer(value, encodingOrOffset, length) + } + + if (typeof SharedArrayBuffer !== 'undefined' && + (isInstance(value, SharedArrayBuffer) || + (value && isInstance(value.buffer, SharedArrayBuffer)))) { + return fromArrayBuffer(value, encodingOrOffset, length) + } + + if (typeof value === 'number') { + throw new TypeError( + 'The "value" argument must not be of type number. Received type number' + ) + } + + const valueOf = value.valueOf && value.valueOf() + if (valueOf != null && valueOf !== value) { + return Buffer.from(valueOf, encodingOrOffset, length) + } + + const b = fromObject(value) + if (b) return b + + if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null && + typeof value[Symbol.toPrimitive] === 'function') { + return Buffer.from(value[Symbol.toPrimitive]('string'), encodingOrOffset, length) + } + + throw new TypeError( + 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + + 'or Array-like Object. Received type ' + (typeof value) + ) +} + +/** + * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError + * if value is a number. + * Buffer.from(str[, encoding]) + * Buffer.from(array) + * Buffer.from(buffer) + * Buffer.from(arrayBuffer[, byteOffset[, length]]) + **/ +Buffer.from = function (value, encodingOrOffset, length) { + return from(value, encodingOrOffset, length) +} + +// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug: +// https://github.com/feross/buffer/pull/148 +Object.setPrototypeOf(Buffer.prototype, Uint8Array.prototype) +Object.setPrototypeOf(Buffer, Uint8Array) + +function assertSize (size) { + if (typeof size !== 'number') { + throw new TypeError('"size" argument must be of type number') + } else if (size < 0) { + throw new RangeError('The value "' + size + '" is invalid for option "size"') + } +} + +function alloc (size, fill, encoding) { + assertSize(size) + if (size <= 0) { + return createBuffer(size) + } + if (fill !== undefined) { + // Only pay attention to encoding if it's a string. This + // prevents accidentally sending in a number that would + // be interpreted as a start offset. + return typeof encoding === 'string' + ? createBuffer(size).fill(fill, encoding) + : createBuffer(size).fill(fill) + } + return createBuffer(size) +} + +/** + * Creates a new filled Buffer instance. + * alloc(size[, fill[, encoding]]) + **/ +Buffer.alloc = function (size, fill, encoding) { + return alloc(size, fill, encoding) +} + +function allocUnsafe (size) { + assertSize(size) + return createBuffer(size < 0 ? 0 : checked(size) | 0) +} + +/** + * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. + * */ +Buffer.allocUnsafe = function (size) { + return allocUnsafe(size) +} +/** + * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. + */ +Buffer.allocUnsafeSlow = function (size) { + return allocUnsafe(size) +} + +function fromString (string, encoding) { + if (typeof encoding !== 'string' || encoding === '') { + encoding = 'utf8' + } + + if (!Buffer.isEncoding(encoding)) { + throw new TypeError('Unknown encoding: ' + encoding) + } + + const length = byteLength(string, encoding) | 0 + let buf = createBuffer(length) + + const actual = buf.write(string, encoding) + + if (actual !== length) { + // Writing a hex string, for example, that contains invalid characters will + // cause everything after the first invalid character to be ignored. (e.g. + // 'abxxcd' will be treated as 'ab') + buf = buf.slice(0, actual) + } + + return buf +} + +function fromArrayLike (array) { + const length = array.length < 0 ? 0 : checked(array.length) | 0 + const buf = createBuffer(length) + for (let i = 0; i < length; i += 1) { + buf[i] = array[i] & 255 + } + return buf +} + +function fromArrayView (arrayView) { + if (isInstance(arrayView, Uint8Array)) { + const copy = new Uint8Array(arrayView) + return fromArrayBuffer(copy.buffer, copy.byteOffset, copy.byteLength) + } + return fromArrayLike(arrayView) +} + +function fromArrayBuffer (array, byteOffset, length) { + if (byteOffset < 0 || array.byteLength < byteOffset) { + throw new RangeError('"offset" is outside of buffer bounds') + } + + if (array.byteLength < byteOffset + (length || 0)) { + throw new RangeError('"length" is outside of buffer bounds') + } + + let buf + if (byteOffset === undefined && length === undefined) { + buf = new Uint8Array(array) + } else if (length === undefined) { + buf = new Uint8Array(array, byteOffset) + } else { + buf = new Uint8Array(array, byteOffset, length) + } + + // Return an augmented `Uint8Array` instance + Object.setPrototypeOf(buf, Buffer.prototype) + + return buf +} + +function fromObject (obj) { + if (Buffer.isBuffer(obj)) { + const len = checked(obj.length) | 0 + const buf = createBuffer(len) + + if (buf.length === 0) { + return buf + } + + obj.copy(buf, 0, 0, len) + return buf + } + + if (obj.length !== undefined) { + if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { + return createBuffer(0) + } + return fromArrayLike(obj) + } + + if (obj.type === 'Buffer' && Array.isArray(obj.data)) { + return fromArrayLike(obj.data) + } +} + +function checked (length) { + // Note: cannot use `length < K_MAX_LENGTH` here because that fails when + // length is NaN (which is otherwise coerced to zero.) + if (length >= K_MAX_LENGTH) { + throw new RangeError('Attempt to allocate Buffer larger than maximum ' + + 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes') + } + return length | 0 +} + +function SlowBuffer (length) { + if (+length != length) { // eslint-disable-line eqeqeq + length = 0 + } + return Buffer.alloc(+length) +} + +Buffer.isBuffer = function isBuffer (b) { + return b != null && b._isBuffer === true && + b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false +} + +Buffer.compare = function compare (a, b) { + if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength) + if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength) + if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { + throw new TypeError( + 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array' + ) + } + + if (a === b) return 0 + + let x = a.length + let y = b.length + + for (let i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i] + y = b[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +Buffer.isEncoding = function isEncoding (encoding) { + switch (String(encoding).toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'latin1': + case 'binary': + case 'base64': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return true + default: + return false + } +} + +Buffer.concat = function concat (list, length) { + if (!Array.isArray(list)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } + + if (list.length === 0) { + return Buffer.alloc(0) + } + + let i + if (length === undefined) { + length = 0 + for (i = 0; i < list.length; ++i) { + length += list[i].length + } + } + + const buffer = Buffer.allocUnsafe(length) + let pos = 0 + for (i = 0; i < list.length; ++i) { + let buf = list[i] + if (isInstance(buf, Uint8Array)) { + if (pos + buf.length > buffer.length) { + if (!Buffer.isBuffer(buf)) buf = Buffer.from(buf) + buf.copy(buffer, pos) + } else { + Uint8Array.prototype.set.call( + buffer, + buf, + pos + ) + } + } else if (!Buffer.isBuffer(buf)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } else { + buf.copy(buffer, pos) + } + pos += buf.length + } + return buffer +} + +function byteLength (string, encoding) { + if (Buffer.isBuffer(string)) { + return string.length + } + if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) { + return string.byteLength + } + if (typeof string !== 'string') { + throw new TypeError( + 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' + + 'Received type ' + typeof string + ) + } + + const len = string.length + const mustMatch = (arguments.length > 2 && arguments[2] === true) + if (!mustMatch && len === 0) return 0 + + // Use a for loop to avoid recursion + let loweredCase = false + for (;;) { + switch (encoding) { + case 'ascii': + case 'latin1': + case 'binary': + return len + case 'utf8': + case 'utf-8': + return utf8ToBytes(string).length + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return len * 2 + case 'hex': + return len >>> 1 + case 'base64': + return base64ToBytes(string).length + default: + if (loweredCase) { + return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8 + } + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} +Buffer.byteLength = byteLength + +function slowToString (encoding, start, end) { + let loweredCase = false + + // No need to verify that "this.length <= MAX_UINT32" since it's a read-only + // property of a typed array. + + // This behaves neither like String nor Uint8Array in that we set start/end + // to their upper/lower bounds if the value passed is out of range. + // undefined is handled specially as per ECMA-262 6th Edition, + // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. + if (start === undefined || start < 0) { + start = 0 + } + // Return early if start > this.length. Done here to prevent potential uint32 + // coercion fail below. + if (start > this.length) { + return '' + } + + if (end === undefined || end > this.length) { + end = this.length + } + + if (end <= 0) { + return '' + } + + // Force coercion to uint32. This will also coerce falsey/NaN values to 0. + end >>>= 0 + start >>>= 0 + + if (end <= start) { + return '' + } + + if (!encoding) encoding = 'utf8' + + while (true) { + switch (encoding) { + case 'hex': + return hexSlice(this, start, end) + + case 'utf8': + case 'utf-8': + return utf8Slice(this, start, end) + + case 'ascii': + return asciiSlice(this, start, end) + + case 'latin1': + case 'binary': + return latin1Slice(this, start, end) + + case 'base64': + return base64Slice(this, start, end) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return utf16leSlice(this, start, end) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = (encoding + '').toLowerCase() + loweredCase = true + } + } +} + +// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package) +// to detect a Buffer instance. It's not possible to use `instanceof Buffer` +// reliably in a browserify context because there could be multiple different +// copies of the 'buffer' package in use. This method works even for Buffer +// instances that were created from another copy of the `buffer` package. +// See: https://github.com/feross/buffer/issues/154 +Buffer.prototype._isBuffer = true + +function swap (b, n, m) { + const i = b[n] + b[n] = b[m] + b[m] = i +} + +Buffer.prototype.swap16 = function swap16 () { + const len = this.length + if (len % 2 !== 0) { + throw new RangeError('Buffer size must be a multiple of 16-bits') + } + for (let i = 0; i < len; i += 2) { + swap(this, i, i + 1) + } + return this +} + +Buffer.prototype.swap32 = function swap32 () { + const len = this.length + if (len % 4 !== 0) { + throw new RangeError('Buffer size must be a multiple of 32-bits') + } + for (let i = 0; i < len; i += 4) { + swap(this, i, i + 3) + swap(this, i + 1, i + 2) + } + return this +} + +Buffer.prototype.swap64 = function swap64 () { + const len = this.length + if (len % 8 !== 0) { + throw new RangeError('Buffer size must be a multiple of 64-bits') + } + for (let i = 0; i < len; i += 8) { + swap(this, i, i + 7) + swap(this, i + 1, i + 6) + swap(this, i + 2, i + 5) + swap(this, i + 3, i + 4) + } + return this +} + +Buffer.prototype.toString = function toString () { + const length = this.length + if (length === 0) return '' + if (arguments.length === 0) return utf8Slice(this, 0, length) + return slowToString.apply(this, arguments) +} + +Buffer.prototype.toLocaleString = Buffer.prototype.toString + +Buffer.prototype.equals = function equals (b) { + if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') + if (this === b) return true + return Buffer.compare(this, b) === 0 +} + +Buffer.prototype.inspect = function inspect () { + let str = '' + const max = exports.INSPECT_MAX_BYTES + str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim() + if (this.length > max) str += ' ... ' + return '' +} +if (customInspectSymbol) { + Buffer.prototype[customInspectSymbol] = Buffer.prototype.inspect +} + +Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { + if (isInstance(target, Uint8Array)) { + target = Buffer.from(target, target.offset, target.byteLength) + } + if (!Buffer.isBuffer(target)) { + throw new TypeError( + 'The "target" argument must be one of type Buffer or Uint8Array. ' + + 'Received type ' + (typeof target) + ) + } + + if (start === undefined) { + start = 0 + } + if (end === undefined) { + end = target ? target.length : 0 + } + if (thisStart === undefined) { + thisStart = 0 + } + if (thisEnd === undefined) { + thisEnd = this.length + } + + if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { + throw new RangeError('out of range index') + } + + if (thisStart >= thisEnd && start >= end) { + return 0 + } + if (thisStart >= thisEnd) { + return -1 + } + if (start >= end) { + return 1 + } + + start >>>= 0 + end >>>= 0 + thisStart >>>= 0 + thisEnd >>>= 0 + + if (this === target) return 0 + + let x = thisEnd - thisStart + let y = end - start + const len = Math.min(x, y) + + const thisCopy = this.slice(thisStart, thisEnd) + const targetCopy = target.slice(start, end) + + for (let i = 0; i < len; ++i) { + if (thisCopy[i] !== targetCopy[i]) { + x = thisCopy[i] + y = targetCopy[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, +// OR the last index of `val` in `buffer` at offset <= `byteOffset`. +// +// Arguments: +// - buffer - a Buffer to search +// - val - a string, Buffer, or number +// - byteOffset - an index into `buffer`; will be clamped to an int32 +// - encoding - an optional encoding, relevant is val is a string +// - dir - true for indexOf, false for lastIndexOf +function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) { + // Empty buffer means no match + if (buffer.length === 0) return -1 + + // Normalize byteOffset + if (typeof byteOffset === 'string') { + encoding = byteOffset + byteOffset = 0 + } else if (byteOffset > 0x7fffffff) { + byteOffset = 0x7fffffff + } else if (byteOffset < -0x80000000) { + byteOffset = -0x80000000 + } + byteOffset = +byteOffset // Coerce to Number. + if (numberIsNaN(byteOffset)) { + // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer + byteOffset = dir ? 0 : (buffer.length - 1) + } + + // Normalize byteOffset: negative offsets start from the end of the buffer + if (byteOffset < 0) byteOffset = buffer.length + byteOffset + if (byteOffset >= buffer.length) { + if (dir) return -1 + else byteOffset = buffer.length - 1 + } else if (byteOffset < 0) { + if (dir) byteOffset = 0 + else return -1 + } + + // Normalize val + if (typeof val === 'string') { + val = Buffer.from(val, encoding) + } + + // Finally, search either indexOf (if dir is true) or lastIndexOf + if (Buffer.isBuffer(val)) { + // Special case: looking for empty string/buffer always fails + if (val.length === 0) { + return -1 + } + return arrayIndexOf(buffer, val, byteOffset, encoding, dir) + } else if (typeof val === 'number') { + val = val & 0xFF // Search for a byte value [0-255] + if (typeof Uint8Array.prototype.indexOf === 'function') { + if (dir) { + return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) + } else { + return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) + } + } + return arrayIndexOf(buffer, [val], byteOffset, encoding, dir) + } + + throw new TypeError('val must be string, number or Buffer') +} + +function arrayIndexOf (arr, val, byteOffset, encoding, dir) { + let indexSize = 1 + let arrLength = arr.length + let valLength = val.length + + if (encoding !== undefined) { + encoding = String(encoding).toLowerCase() + if (encoding === 'ucs2' || encoding === 'ucs-2' || + encoding === 'utf16le' || encoding === 'utf-16le') { + if (arr.length < 2 || val.length < 2) { + return -1 + } + indexSize = 2 + arrLength /= 2 + valLength /= 2 + byteOffset /= 2 + } + } + + function read (buf, i) { + if (indexSize === 1) { + return buf[i] + } else { + return buf.readUInt16BE(i * indexSize) + } + } + + let i + if (dir) { + let foundIndex = -1 + for (i = byteOffset; i < arrLength; i++) { + if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { + if (foundIndex === -1) foundIndex = i + if (i - foundIndex + 1 === valLength) return foundIndex * indexSize + } else { + if (foundIndex !== -1) i -= i - foundIndex + foundIndex = -1 + } + } + } else { + if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength + for (i = byteOffset; i >= 0; i--) { + let found = true + for (let j = 0; j < valLength; j++) { + if (read(arr, i + j) !== read(val, j)) { + found = false + break + } + } + if (found) return i + } + } + + return -1 +} + +Buffer.prototype.includes = function includes (val, byteOffset, encoding) { + return this.indexOf(val, byteOffset, encoding) !== -1 +} + +Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, true) +} + +Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, false) +} + +function hexWrite (buf, string, offset, length) { + offset = Number(offset) || 0 + const remaining = buf.length - offset + if (!length) { + length = remaining + } else { + length = Number(length) + if (length > remaining) { + length = remaining + } + } + + const strLen = string.length + + if (length > strLen / 2) { + length = strLen / 2 + } + let i + for (i = 0; i < length; ++i) { + const parsed = parseInt(string.substr(i * 2, 2), 16) + if (numberIsNaN(parsed)) return i + buf[offset + i] = parsed + } + return i +} + +function utf8Write (buf, string, offset, length) { + return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) +} + +function asciiWrite (buf, string, offset, length) { + return blitBuffer(asciiToBytes(string), buf, offset, length) +} + +function base64Write (buf, string, offset, length) { + return blitBuffer(base64ToBytes(string), buf, offset, length) +} + +function ucs2Write (buf, string, offset, length) { + return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) +} + +Buffer.prototype.write = function write (string, offset, length, encoding) { + // Buffer#write(string) + if (offset === undefined) { + encoding = 'utf8' + length = this.length + offset = 0 + // Buffer#write(string, encoding) + } else if (length === undefined && typeof offset === 'string') { + encoding = offset + length = this.length + offset = 0 + // Buffer#write(string, offset[, length][, encoding]) + } else if (isFinite(offset)) { + offset = offset >>> 0 + if (isFinite(length)) { + length = length >>> 0 + if (encoding === undefined) encoding = 'utf8' + } else { + encoding = length + length = undefined + } + } else { + throw new Error( + 'Buffer.write(string, encoding, offset[, length]) is no longer supported' + ) + } + + const remaining = this.length - offset + if (length === undefined || length > remaining) length = remaining + + if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { + throw new RangeError('Attempt to write outside buffer bounds') + } + + if (!encoding) encoding = 'utf8' + + let loweredCase = false + for (;;) { + switch (encoding) { + case 'hex': + return hexWrite(this, string, offset, length) + + case 'utf8': + case 'utf-8': + return utf8Write(this, string, offset, length) + + case 'ascii': + case 'latin1': + case 'binary': + return asciiWrite(this, string, offset, length) + + case 'base64': + // Warning: maxLength not taken into account in base64Write + return base64Write(this, string, offset, length) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return ucs2Write(this, string, offset, length) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} + +Buffer.prototype.toJSON = function toJSON () { + return { + type: 'Buffer', + data: Array.prototype.slice.call(this._arr || this, 0) + } +} + +function base64Slice (buf, start, end) { + if (start === 0 && end === buf.length) { + return base64.fromByteArray(buf) + } else { + return base64.fromByteArray(buf.slice(start, end)) + } +} + +function utf8Slice (buf, start, end) { + end = Math.min(buf.length, end) + const res = [] + + let i = start + while (i < end) { + const firstByte = buf[i] + let codePoint = null + let bytesPerSequence = (firstByte > 0xEF) + ? 4 + : (firstByte > 0xDF) + ? 3 + : (firstByte > 0xBF) + ? 2 + : 1 + + if (i + bytesPerSequence <= end) { + let secondByte, thirdByte, fourthByte, tempCodePoint + + switch (bytesPerSequence) { + case 1: + if (firstByte < 0x80) { + codePoint = firstByte + } + break + case 2: + secondByte = buf[i + 1] + if ((secondByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) + if (tempCodePoint > 0x7F) { + codePoint = tempCodePoint + } + } + break + case 3: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) + if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { + codePoint = tempCodePoint + } + } + break + case 4: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + fourthByte = buf[i + 3] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) + if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { + codePoint = tempCodePoint + } + } + } + } + + if (codePoint === null) { + // we did not generate a valid codePoint so insert a + // replacement char (U+FFFD) and advance only 1 byte + codePoint = 0xFFFD + bytesPerSequence = 1 + } else if (codePoint > 0xFFFF) { + // encode to utf16 (surrogate pair dance) + codePoint -= 0x10000 + res.push(codePoint >>> 10 & 0x3FF | 0xD800) + codePoint = 0xDC00 | codePoint & 0x3FF + } + + res.push(codePoint) + i += bytesPerSequence + } + + return decodeCodePointsArray(res) +} + +// Based on http://stackoverflow.com/a/22747272/680742, the browser with +// the lowest limit is Chrome, with 0x10000 args. +// We go 1 magnitude less, for safety +const MAX_ARGUMENTS_LENGTH = 0x1000 + +function decodeCodePointsArray (codePoints) { + const len = codePoints.length + if (len <= MAX_ARGUMENTS_LENGTH) { + return String.fromCharCode.apply(String, codePoints) // avoid extra slice() + } + + // Decode in chunks to avoid "call stack size exceeded". + let res = '' + let i = 0 + while (i < len) { + res += String.fromCharCode.apply( + String, + codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) + ) + } + return res +} + +function asciiSlice (buf, start, end) { + let ret = '' + end = Math.min(buf.length, end) + + for (let i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i] & 0x7F) + } + return ret +} + +function latin1Slice (buf, start, end) { + let ret = '' + end = Math.min(buf.length, end) + + for (let i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i]) + } + return ret +} + +function hexSlice (buf, start, end) { + const len = buf.length + + if (!start || start < 0) start = 0 + if (!end || end < 0 || end > len) end = len + + let out = '' + for (let i = start; i < end; ++i) { + out += hexSliceLookupTable[buf[i]] + } + return out +} + +function utf16leSlice (buf, start, end) { + const bytes = buf.slice(start, end) + let res = '' + // If bytes.length is odd, the last 8 bits must be ignored (same as node.js) + for (let i = 0; i < bytes.length - 1; i += 2) { + res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256)) + } + return res +} + +Buffer.prototype.slice = function slice (start, end) { + const len = this.length + start = ~~start + end = end === undefined ? len : ~~end + + if (start < 0) { + start += len + if (start < 0) start = 0 + } else if (start > len) { + start = len + } + + if (end < 0) { + end += len + if (end < 0) end = 0 + } else if (end > len) { + end = len + } + + if (end < start) end = start + + const newBuf = this.subarray(start, end) + // Return an augmented `Uint8Array` instance + Object.setPrototypeOf(newBuf, Buffer.prototype) + + return newBuf +} + +/* + * Need to make sure that buffer isn't trying to write out of bounds. + */ +function checkOffset (offset, ext, length) { + if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') + if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') +} + +Buffer.prototype.readUintLE = +Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + let val = this[offset] + let mul = 1 + let i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + + return val +} + +Buffer.prototype.readUintBE = +Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + checkOffset(offset, byteLength, this.length) + } + + let val = this[offset + --byteLength] + let mul = 1 + while (byteLength > 0 && (mul *= 0x100)) { + val += this[offset + --byteLength] * mul + } + + return val +} + +Buffer.prototype.readUint8 = +Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 1, this.length) + return this[offset] +} + +Buffer.prototype.readUint16LE = +Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + return this[offset] | (this[offset + 1] << 8) +} + +Buffer.prototype.readUint16BE = +Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + return (this[offset] << 8) | this[offset + 1] +} + +Buffer.prototype.readUint32LE = +Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return ((this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16)) + + (this[offset + 3] * 0x1000000) +} + +Buffer.prototype.readUint32BE = +Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] * 0x1000000) + + ((this[offset + 1] << 16) | + (this[offset + 2] << 8) | + this[offset + 3]) +} + +Buffer.prototype.readBigUInt64LE = defineBigIntMethod(function readBigUInt64LE (offset) { + offset = offset >>> 0 + validateNumber(offset, 'offset') + const first = this[offset] + const last = this[offset + 7] + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8) + } + + const lo = first + + this[++offset] * 2 ** 8 + + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 24 + + const hi = this[++offset] + + this[++offset] * 2 ** 8 + + this[++offset] * 2 ** 16 + + last * 2 ** 24 + + return BigInt(lo) + (BigInt(hi) << BigInt(32)) +}) + +Buffer.prototype.readBigUInt64BE = defineBigIntMethod(function readBigUInt64BE (offset) { + offset = offset >>> 0 + validateNumber(offset, 'offset') + const first = this[offset] + const last = this[offset + 7] + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8) + } + + const hi = first * 2 ** 24 + + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + + this[++offset] + + const lo = this[++offset] * 2 ** 24 + + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + + last + + return (BigInt(hi) << BigInt(32)) + BigInt(lo) +}) + +Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + let val = this[offset] + let mul = 1 + let i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + let i = byteLength + let mul = 1 + let val = this[offset + --i] + while (i > 0 && (mul *= 0x100)) { + val += this[offset + --i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 1, this.length) + if (!(this[offset] & 0x80)) return (this[offset]) + return ((0xff - this[offset] + 1) * -1) +} + +Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + const val = this[offset] | (this[offset + 1] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + const val = this[offset + 1] | (this[offset] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16) | + (this[offset + 3] << 24) +} + +Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] << 24) | + (this[offset + 1] << 16) | + (this[offset + 2] << 8) | + (this[offset + 3]) +} + +Buffer.prototype.readBigInt64LE = defineBigIntMethod(function readBigInt64LE (offset) { + offset = offset >>> 0 + validateNumber(offset, 'offset') + const first = this[offset] + const last = this[offset + 7] + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8) + } + + const val = this[offset + 4] + + this[offset + 5] * 2 ** 8 + + this[offset + 6] * 2 ** 16 + + (last << 24) // Overflow + + return (BigInt(val) << BigInt(32)) + + BigInt(first + + this[++offset] * 2 ** 8 + + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 24) +}) + +Buffer.prototype.readBigInt64BE = defineBigIntMethod(function readBigInt64BE (offset) { + offset = offset >>> 0 + validateNumber(offset, 'offset') + const first = this[offset] + const last = this[offset + 7] + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8) + } + + const val = (first << 24) + // Overflow + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + + this[++offset] + + return (BigInt(val) << BigInt(32)) + + BigInt(this[++offset] * 2 ** 24 + + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + + last) +}) + +Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, true, 23, 4) +} + +Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, false, 23, 4) +} + +Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, true, 52, 8) +} + +Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, false, 52, 8) +} + +function checkInt (buf, value, offset, ext, max, min) { + if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') + if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') + if (offset + ext > buf.length) throw new RangeError('Index out of range') +} + +Buffer.prototype.writeUintLE = +Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + const maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } + + let mul = 1 + let i = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUintBE = +Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + const maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } + + let i = byteLength - 1 + let mul = 1 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUint8 = +Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) + this[offset] = (value & 0xff) + return offset + 1 +} + +Buffer.prototype.writeUint16LE = +Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + return offset + 2 +} + +Buffer.prototype.writeUint16BE = +Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + return offset + 2 +} + +Buffer.prototype.writeUint32LE = +Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + this[offset + 3] = (value >>> 24) + this[offset + 2] = (value >>> 16) + this[offset + 1] = (value >>> 8) + this[offset] = (value & 0xff) + return offset + 4 +} + +Buffer.prototype.writeUint32BE = +Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + return offset + 4 +} + +function wrtBigUInt64LE (buf, value, offset, min, max) { + checkIntBI(value, min, max, buf, offset, 7) + + let lo = Number(value & BigInt(0xffffffff)) + buf[offset++] = lo + lo = lo >> 8 + buf[offset++] = lo + lo = lo >> 8 + buf[offset++] = lo + lo = lo >> 8 + buf[offset++] = lo + let hi = Number(value >> BigInt(32) & BigInt(0xffffffff)) + buf[offset++] = hi + hi = hi >> 8 + buf[offset++] = hi + hi = hi >> 8 + buf[offset++] = hi + hi = hi >> 8 + buf[offset++] = hi + return offset +} + +function wrtBigUInt64BE (buf, value, offset, min, max) { + checkIntBI(value, min, max, buf, offset, 7) + + let lo = Number(value & BigInt(0xffffffff)) + buf[offset + 7] = lo + lo = lo >> 8 + buf[offset + 6] = lo + lo = lo >> 8 + buf[offset + 5] = lo + lo = lo >> 8 + buf[offset + 4] = lo + let hi = Number(value >> BigInt(32) & BigInt(0xffffffff)) + buf[offset + 3] = hi + hi = hi >> 8 + buf[offset + 2] = hi + hi = hi >> 8 + buf[offset + 1] = hi + hi = hi >> 8 + buf[offset] = hi + return offset + 8 +} + +Buffer.prototype.writeBigUInt64LE = defineBigIntMethod(function writeBigUInt64LE (value, offset = 0) { + return wrtBigUInt64LE(this, value, offset, BigInt(0), BigInt('0xffffffffffffffff')) +}) + +Buffer.prototype.writeBigUInt64BE = defineBigIntMethod(function writeBigUInt64BE (value, offset = 0) { + return wrtBigUInt64BE(this, value, offset, BigInt(0), BigInt('0xffffffffffffffff')) +}) + +Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + const limit = Math.pow(2, (8 * byteLength) - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + let i = 0 + let mul = 1 + let sub = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { + sub = 1 + } + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + const limit = Math.pow(2, (8 * byteLength) - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + let i = byteLength - 1 + let mul = 1 + let sub = 0 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { + sub = 1 + } + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) + if (value < 0) value = 0xff + value + 1 + this[offset] = (value & 0xff) + return offset + 1 +} + +Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + return offset + 2 +} + +Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + return offset + 2 +} + +Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + this[offset + 2] = (value >>> 16) + this[offset + 3] = (value >>> 24) + return offset + 4 +} + +Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + if (value < 0) value = 0xffffffff + value + 1 + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + return offset + 4 +} + +Buffer.prototype.writeBigInt64LE = defineBigIntMethod(function writeBigInt64LE (value, offset = 0) { + return wrtBigUInt64LE(this, value, offset, -BigInt('0x8000000000000000'), BigInt('0x7fffffffffffffff')) +}) + +Buffer.prototype.writeBigInt64BE = defineBigIntMethod(function writeBigInt64BE (value, offset = 0) { + return wrtBigUInt64BE(this, value, offset, -BigInt('0x8000000000000000'), BigInt('0x7fffffffffffffff')) +}) + +function checkIEEE754 (buf, value, offset, ext, max, min) { + if (offset + ext > buf.length) throw new RangeError('Index out of range') + if (offset < 0) throw new RangeError('Index out of range') +} + +function writeFloat (buf, value, offset, littleEndian, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) + } + ieee754.write(buf, value, offset, littleEndian, 23, 4) + return offset + 4 +} + +Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { + return writeFloat(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { + return writeFloat(this, value, offset, false, noAssert) +} + +function writeDouble (buf, value, offset, littleEndian, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) + } + ieee754.write(buf, value, offset, littleEndian, 52, 8) + return offset + 8 +} + +Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { + return writeDouble(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { + return writeDouble(this, value, offset, false, noAssert) +} + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function copy (target, targetStart, start, end) { + if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer') + if (!start) start = 0 + if (!end && end !== 0) end = this.length + if (targetStart >= target.length) targetStart = target.length + if (!targetStart) targetStart = 0 + if (end > 0 && end < start) end = start + + // Copy 0 bytes; we're done + if (end === start) return 0 + if (target.length === 0 || this.length === 0) return 0 + + // Fatal error conditions + if (targetStart < 0) { + throw new RangeError('targetStart out of bounds') + } + if (start < 0 || start >= this.length) throw new RangeError('Index out of range') + if (end < 0) throw new RangeError('sourceEnd out of bounds') + + // Are we oob? + if (end > this.length) end = this.length + if (target.length - targetStart < end - start) { + end = target.length - targetStart + start + } + + const len = end - start + + if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') { + // Use built-in when available, missing from IE11 + this.copyWithin(targetStart, start, end) + } else { + Uint8Array.prototype.set.call( + target, + this.subarray(start, end), + targetStart + ) + } + + return len +} + +// Usage: +// buffer.fill(number[, offset[, end]]) +// buffer.fill(buffer[, offset[, end]]) +// buffer.fill(string[, offset[, end]][, encoding]) +Buffer.prototype.fill = function fill (val, start, end, encoding) { + // Handle string cases: + if (typeof val === 'string') { + if (typeof start === 'string') { + encoding = start + start = 0 + end = this.length + } else if (typeof end === 'string') { + encoding = end + end = this.length + } + if (encoding !== undefined && typeof encoding !== 'string') { + throw new TypeError('encoding must be a string') + } + if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { + throw new TypeError('Unknown encoding: ' + encoding) + } + if (val.length === 1) { + const code = val.charCodeAt(0) + if ((encoding === 'utf8' && code < 128) || + encoding === 'latin1') { + // Fast path: If `val` fits into a single byte, use that numeric value. + val = code + } + } + } else if (typeof val === 'number') { + val = val & 255 + } else if (typeof val === 'boolean') { + val = Number(val) + } + + // Invalid ranges are not set to a default, so can range check early. + if (start < 0 || this.length < start || this.length < end) { + throw new RangeError('Out of range index') + } + + if (end <= start) { + return this + } + + start = start >>> 0 + end = end === undefined ? this.length : end >>> 0 + + if (!val) val = 0 + + let i + if (typeof val === 'number') { + for (i = start; i < end; ++i) { + this[i] = val + } + } else { + const bytes = Buffer.isBuffer(val) + ? val + : Buffer.from(val, encoding) + const len = bytes.length + if (len === 0) { + throw new TypeError('The value "' + val + + '" is invalid for argument "value"') + } + for (i = 0; i < end - start; ++i) { + this[i + start] = bytes[i % len] + } + } + + return this +} + +// CUSTOM ERRORS +// ============= + +// Simplified versions from Node, changed for Buffer-only usage +const errors = {} +function E (sym, getMessage, Base) { + errors[sym] = class NodeError extends Base { + constructor () { + super() + + Object.defineProperty(this, 'message', { + value: getMessage.apply(this, arguments), + writable: true, + configurable: true + }) + + // Add the error code to the name to include it in the stack trace. + this.name = `${this.name} [${sym}]` + // Access the stack to generate the error message including the error code + // from the name. + this.stack // eslint-disable-line no-unused-expressions + // Reset the name to the actual name. + delete this.name + } + + get code () { + return sym + } + + set code (value) { + Object.defineProperty(this, 'code', { + configurable: true, + enumerable: true, + value, + writable: true + }) + } + + toString () { + return `${this.name} [${sym}]: ${this.message}` + } + } +} + +E('ERR_BUFFER_OUT_OF_BOUNDS', + function (name) { + if (name) { + return `${name} is outside of buffer bounds` + } + + return 'Attempt to access memory outside buffer bounds' + }, RangeError) +E('ERR_INVALID_ARG_TYPE', + function (name, actual) { + return `The "${name}" argument must be of type number. Received type ${typeof actual}` + }, TypeError) +E('ERR_OUT_OF_RANGE', + function (str, range, input) { + let msg = `The value of "${str}" is out of range.` + let received = input + if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) { + received = addNumericalSeparator(String(input)) + } else if (typeof input === 'bigint') { + received = String(input) + if (input > BigInt(2) ** BigInt(32) || input < -(BigInt(2) ** BigInt(32))) { + received = addNumericalSeparator(received) + } + received += 'n' + } + msg += ` It must be ${range}. Received ${received}` + return msg + }, RangeError) + +function addNumericalSeparator (val) { + let res = '' + let i = val.length + const start = val[0] === '-' ? 1 : 0 + for (; i >= start + 4; i -= 3) { + res = `_${val.slice(i - 3, i)}${res}` + } + return `${val.slice(0, i)}${res}` +} + +// CHECK FUNCTIONS +// =============== + +function checkBounds (buf, offset, byteLength) { + validateNumber(offset, 'offset') + if (buf[offset] === undefined || buf[offset + byteLength] === undefined) { + boundsError(offset, buf.length - (byteLength + 1)) + } +} + +function checkIntBI (value, min, max, buf, offset, byteLength) { + if (value > max || value < min) { + const n = typeof min === 'bigint' ? 'n' : '' + let range + if (byteLength > 3) { + if (min === 0 || min === BigInt(0)) { + range = `>= 0${n} and < 2${n} ** ${(byteLength + 1) * 8}${n}` + } else { + range = `>= -(2${n} ** ${(byteLength + 1) * 8 - 1}${n}) and < 2 ** ` + + `${(byteLength + 1) * 8 - 1}${n}` + } + } else { + range = `>= ${min}${n} and <= ${max}${n}` + } + throw new errors.ERR_OUT_OF_RANGE('value', range, value) + } + checkBounds(buf, offset, byteLength) +} + +function validateNumber (value, name) { + if (typeof value !== 'number') { + throw new errors.ERR_INVALID_ARG_TYPE(name, 'number', value) + } +} + +function boundsError (value, length, type) { + if (Math.floor(value) !== value) { + validateNumber(value, type) + throw new errors.ERR_OUT_OF_RANGE(type || 'offset', 'an integer', value) + } + + if (length < 0) { + throw new errors.ERR_BUFFER_OUT_OF_BOUNDS() + } + + throw new errors.ERR_OUT_OF_RANGE(type || 'offset', + `>= ${type ? 1 : 0} and <= ${length}`, + value) +} + +// HELPER FUNCTIONS +// ================ + +const INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g + +function base64clean (str) { + // Node takes equal signs as end of the Base64 encoding + str = str.split('=')[0] + // Node strips out invalid characters like \n and \t from the string, base64-js does not + str = str.trim().replace(INVALID_BASE64_RE, '') + // Node converts strings with length < 2 to '' + if (str.length < 2) return '' + // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not + while (str.length % 4 !== 0) { + str = str + '=' + } + return str +} + +function utf8ToBytes (string, units) { + units = units || Infinity + let codePoint + const length = string.length + let leadSurrogate = null + const bytes = [] + + for (let i = 0; i < length; ++i) { + codePoint = string.charCodeAt(i) + + // is surrogate component + if (codePoint > 0xD7FF && codePoint < 0xE000) { + // last char was a lead + if (!leadSurrogate) { + // no lead yet + if (codePoint > 0xDBFF) { + // unexpected trail + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } else if (i + 1 === length) { + // unpaired lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } + + // valid lead + leadSurrogate = codePoint + + continue + } + + // 2 leads in a row + if (codePoint < 0xDC00) { + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + leadSurrogate = codePoint + continue + } + + // valid surrogate pair + codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 + } else if (leadSurrogate) { + // valid bmp char, but last char was a lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + } + + leadSurrogate = null + + // encode utf8 + if (codePoint < 0x80) { + if ((units -= 1) < 0) break + bytes.push(codePoint) + } else if (codePoint < 0x800) { + if ((units -= 2) < 0) break + bytes.push( + codePoint >> 0x6 | 0xC0, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x10000) { + if ((units -= 3) < 0) break + bytes.push( + codePoint >> 0xC | 0xE0, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x110000) { + if ((units -= 4) < 0) break + bytes.push( + codePoint >> 0x12 | 0xF0, + codePoint >> 0xC & 0x3F | 0x80, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else { + throw new Error('Invalid code point') + } + } + + return bytes +} + +function asciiToBytes (str) { + const byteArray = [] + for (let i = 0; i < str.length; ++i) { + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push(str.charCodeAt(i) & 0xFF) + } + return byteArray +} + +function utf16leToBytes (str, units) { + let c, hi, lo + const byteArray = [] + for (let i = 0; i < str.length; ++i) { + if ((units -= 2) < 0) break + + c = str.charCodeAt(i) + hi = c >> 8 + lo = c % 256 + byteArray.push(lo) + byteArray.push(hi) + } + + return byteArray +} + +function base64ToBytes (str) { + return base64.toByteArray(base64clean(str)) +} + +function blitBuffer (src, dst, offset, length) { + let i + for (i = 0; i < length; ++i) { + if ((i + offset >= dst.length) || (i >= src.length)) break + dst[i + offset] = src[i] + } + return i +} + +// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass +// the `instanceof` check but they should be treated as of that type. +// See: https://github.com/feross/buffer/issues/166 +function isInstance (obj, type) { + return obj instanceof type || + (obj != null && obj.constructor != null && obj.constructor.name != null && + obj.constructor.name === type.name) +} +function numberIsNaN (obj) { + // For IE11 support + return obj !== obj // eslint-disable-line no-self-compare +} + +// Create lookup table for `toString('hex')` +// See: https://github.com/feross/buffer/issues/219 +const hexSliceLookupTable = (function () { + const alphabet = '0123456789abcdef' + const table = new Array(256) + for (let i = 0; i < 16; ++i) { + const i16 = i * 16 + for (let j = 0; j < 16; ++j) { + table[i16 + j] = alphabet[i] + alphabet[j] + } + } + return table +})() + +// Return not function with Error if BigInt not supported +function defineBigIntMethod (fn) { + return typeof BigInt === 'undefined' ? BufferBigIntNotDefined : fn +} + +function BufferBigIntNotDefined () { + throw new Error('BigInt not supported') +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/package.json new file mode 100644 index 0000000..ca1ad9a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/buffer/package.json @@ -0,0 +1,93 @@ +{ + "name": "buffer", + "description": "Node.js Buffer API, for the browser", + "version": "6.0.3", + "author": { + "name": "Feross Aboukhadijeh", + "email": "feross@feross.org", + "url": "https://feross.org" + }, + "bugs": { + "url": "https://github.com/feross/buffer/issues" + }, + "contributors": [ + "Romain Beauxis ", + "James Halliday " + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + }, + "devDependencies": { + "airtap": "^3.0.0", + "benchmark": "^2.1.4", + "browserify": "^17.0.0", + "concat-stream": "^2.0.0", + "hyperquest": "^2.1.3", + "is-buffer": "^2.0.5", + "is-nan": "^1.3.0", + "split": "^1.0.1", + "standard": "*", + "tape": "^5.0.1", + "through2": "^4.0.2", + "uglify-js": "^3.11.5" + }, + "homepage": "https://github.com/feross/buffer", + "jspm": { + "map": { + "./index.js": { + "node": "@node/buffer" + } + } + }, + "keywords": [ + "arraybuffer", + "browser", + "browserify", + "buffer", + "compatible", + "dataview", + "uint8array" + ], + "license": "MIT", + "main": "index.js", + "types": "index.d.ts", + "repository": { + "type": "git", + "url": "git://github.com/feross/buffer.git" + }, + "scripts": { + "perf": "browserify --debug perf/bracket-notation.js > perf/bundle.js && open perf/index.html", + "perf-node": "node perf/bracket-notation.js && node perf/concat.js && node perf/copy-big.js && node perf/copy.js && node perf/new-big.js && node perf/new.js && node perf/readDoubleBE.js && node perf/readFloatBE.js && node perf/readUInt32LE.js && node perf/slice.js && node perf/writeFloatBE.js", + "size": "browserify -r ./ | uglifyjs -c -m | gzip | wc -c", + "test": "standard && node ./bin/test.js", + "test-browser-old": "airtap -- test/*.js", + "test-browser-old-local": "airtap --local -- test/*.js", + "test-browser-new": "airtap -- test/*.js test/node/*.js", + "test-browser-new-local": "airtap --local -- test/*.js test/node/*.js", + "test-node": "tape test/*.js test/node/*.js", + "update-authors": "./bin/update-authors.sh" + }, + "standard": { + "ignore": [ + "test/node/**/*.js", + "test/common.js", + "test/_polyfill.js", + "perf/**/*.js" + ] + }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/LICENSE new file mode 100644 index 0000000..6030018 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2018 Vincent Weevers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/index.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/index.d.ts new file mode 100644 index 0000000..51bfb08 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/index.d.ts @@ -0,0 +1,10 @@ +export function fromCallback(callback: Callback | undefined, symbol: S) + : Callback & { [K in S]?: Promise }; + +export function fromCallback(callback: Callback | undefined) + : Callback & { promise?: Promise }; + +export function fromPromise(promise: Promise, callback: Callback | undefined) + : Promise | undefined; + +type Callback = (err: Error | null, result: T) => void; diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/index.js new file mode 100644 index 0000000..696317e --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/index.js @@ -0,0 +1,28 @@ +'use strict' + +var nextTick = require('./next-tick') + +exports.fromCallback = function (callback, symbol) { + if (callback === undefined) { + var promise = new Promise(function (resolve, reject) { + callback = function (err, res) { + if (err) reject(err) + else resolve(res) + } + }) + + callback[symbol !== undefined ? symbol : 'promise'] = promise + } else if (typeof callback !== 'function') { + throw new TypeError('Callback must be a function') + } + + return callback +} + +exports.fromPromise = function (promise, callback) { + if (callback === undefined) return promise + + promise + .then(function (res) { nextTick(() => callback(null, res)) }) + .catch(function (err) { nextTick(() => callback(err)) }) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/next-tick-browser.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/next-tick-browser.js new file mode 100644 index 0000000..e0b8cf3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/next-tick-browser.js @@ -0,0 +1 @@ +module.exports = typeof queueMicrotask === 'function' ? queueMicrotask : (fn) => Promise.resolve().then(fn) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/next-tick.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/next-tick.js new file mode 100644 index 0000000..c6d33ee --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/next-tick.js @@ -0,0 +1 @@ +module.exports = process.nextTick.bind(process) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/package.json new file mode 100644 index 0000000..c04215a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/package.json @@ -0,0 +1,38 @@ +{ + "name": "catering", + "version": "2.1.1", + "description": "Simple utility to allow your module to be consumed with a callback or promise", + "license": "MIT", + "author": "Vincent Weevers", + "scripts": { + "test": "standard && node test.js", + "test-browsers-local": "airtap test.js" + }, + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "next-tick.js", + "next-tick-browser.js" + ], + "browser": { + "./next-tick.js": "./next-tick-browser.js" + }, + "devDependencies": { + "airtap": "^4.0.4", + "airtap-playwright": "^1.0.1", + "standard": "^16.0.4", + "tape": "^5.4.0" + }, + "keywords": [ + "callback", + "promise", + "promisify" + ], + "engines": { + "node": ">=6" + }, + "repository": "vweevers/catering", + "bugs": "https://github.com/vweevers/catering/issues", + "homepage": "https://github.com/vweevers/catering" +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/readme.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/readme.md new file mode 100644 index 0000000..bf74ce1 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/catering/readme.md @@ -0,0 +1,55 @@ +# catering + +**Cater to callback and promise crowds.** +Simple utility to allow your module to be consumed with a callback or promise. For Node.js and browsers. + +[![npm status](https://img.shields.io/npm/v/catering.svg)](https://www.npmjs.com/package/catering) +[![Node version](https://img.shields.io/node/v/catering.svg)](https://www.npmjs.com/package/catering) +[![Test](https://img.shields.io/github/workflow/status/vweevers/catering/Test?label=test)](https://github.com/vweevers/catering/actions/workflows/test.yml) +[![Standard](https://img.shields.io/badge/standard-informational?logo=javascript&logoColor=fff)](https://standardjs.com) + +## Menu + +If your module internally uses callbacks: + +```js +const { fromCallback } = require('catering') +const kPromise = Symbol('promise') + +module.exports = function (callback) { + callback = fromCallback(callback, kPromise) + queueMicrotask(() => callback(null, 'example')) + return callback[kPromise] +} +``` + +If your module internally uses promises: + +```js +const { fromPromise } = require('catering') + +module.exports = function (callback) { + return fromPromise(Promise.resolve('example'), callback) +} +``` + +Either way your module can now be consumed in two ways: + +```js +example((err, result) => {}) +const result = await example() +``` + +When converting from a promise to a callback, `fromPromise` calls the callback in a next tick to escape the promise chain and not let it steal your beautiful errors. + +## Install + +With [npm](https://npmjs.org) do: + +``` +npm install catering +``` + +## License + +[MIT](LICENSE) © 2018-present Vincent Weevers. Originally extracted from [`levelup`](https://github.com/Level/levelup/blob/37e0270c8c29d5086904e29e247e918dddcce6e2/lib/promisify.js). diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/CHANGELOG.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/CHANGELOG.md new file mode 100644 index 0000000..3bbb953 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/CHANGELOG.md @@ -0,0 +1,65 @@ +# Changelog + +## [1.4.1] - 2024-01-20 + +### Fixed + +- Fix race condition in tests ([#90](https://github.com/Level/classic-level/issues/90)) ([`9ff2e82`](https://github.com/Level/classic-level/commit/9ff2e82)) (Matthew Keil). + +## [1.4.0] - 2023-11-26 + +_Not released to npm because of a race issue, which was fixed in 1.4.1._ + +### Added + +- Add opt-in multithreading ([#85](https://github.com/Level/classic-level/issues/85)) ([`7d497a5`](https://github.com/Level/classic-level/commit/7d497a5)) (Matthew Keil). + +## [1.3.0] - 2023-04-07 + +### Changed + +- Refactor some pointer usage ([#25](https://github.com/Level/classic-level/issues/25)) ([`d6437b4`](https://github.com/Level/classic-level/commit/d6437b4)) (Robert Nagy) +- Refactor: handle view encoding (Uint8Array) natively ([#43](https://github.com/Level/classic-level/issues/43)) ([`b9fd5e9`](https://github.com/Level/classic-level/commit/b9fd5e9)) (Vincent Weevers) +- Bump and unlock `napi-macros` from 2.0.0 to 2.2.2 ([#58](https://github.com/Level/classic-level/issues/58)) ([`8a4717b`](https://github.com/Level/classic-level/commit/8a4717b)) (Vincent Weevers). + +### Fixed + +- Swap linux-arm build to use `linux-arm64-lts` ([#71](https://github.com/Level/classic-level/issues/71)) ([`5ea74ab`](https://github.com/Level/classic-level/commit/5ea74ab)) (Cody Swendrowski) +- Add `openssl_fips` variable to gyp bindings ([#72](https://github.com/Level/classic-level/issues/72)) ([`b3f8517`](https://github.com/Level/classic-level/commit/b3f8517)) (Cody Swendrowski). + +## [1.2.0] - 2022-03-25 + +### Added + +- Yield `LEVEL_LOCKED` error when lock is held ([#8](https://github.com/Level/classic-level/issues/8)) ([`aa975de`](https://github.com/Level/classic-level/commit/aa975de)) (Vincent Weevers) + +### Fixed + +- Fix `getMany()` memory leak ([#9](https://github.com/Level/classic-level/issues/9)) ([`00364c7`](https://github.com/Level/classic-level/commit/00364c7)) (Vincent Weevers). + +## [1.1.0] - 2022-03-06 + +### Added + +- Create location directory recursively ([#6](https://github.com/Level/classic-level/issues/6)) ([`1ba0b69`](https://github.com/Level/classic-level/commit/1ba0b69)) (Vincent Weevers) + +### Fixed + +- Fix TypeScript type declarations ([`a79fe82`](https://github.com/Level/classic-level/commit/a79fe82)) (Vincent Weevers) +- Document the return type of `db.batch()` and add example ([`a909ea6`](https://github.com/Level/classic-level/commit/a909ea6)) (Vincent Weevers). + +## [1.0.0] - 2022-03-04 + +_:seedling: Initial release. If you are upgrading from `leveldown` please see [`UPGRADING.md`](UPGRADING.md)._ + +[1.4.1]: https://github.com/Level/classic-level/releases/tag/v1.4.1 + +[1.4.0]: https://github.com/Level/classic-level/releases/tag/v1.4.0 + +[1.3.0]: https://github.com/Level/classic-level/releases/tag/v1.3.0 + +[1.2.0]: https://github.com/Level/classic-level/releases/tag/v1.2.0 + +[1.1.0]: https://github.com/Level/classic-level/releases/tag/v1.1.0 + +[1.0.0]: https://github.com/Level/classic-level/releases/tag/v1.0.0 diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/LICENSE new file mode 100644 index 0000000..f39ce8a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2012 Rod Vagg and the contributors to classic-level and leveldown. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/README.md new file mode 100644 index 0000000..3221bd6 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/README.md @@ -0,0 +1,789 @@ +# classic-level + +**An [`abstract-level`](https://github.com/Level/abstract-level) database backed by [LevelDB](https://github.com/google/leveldb).** The successor to [`leveldown`](https://github.com/Level/leveldown) with builtin encodings, sublevels, events, promises and support of Uint8Array. If you are upgrading please see [`UPGRADING.md`](UPGRADING.md). + +> :pushpin: Which module should I use? What is `abstract-level`? Head over to the [FAQ](https://github.com/Level/community#faq). + +[![level badge][level-badge]](https://github.com/Level/awesome) +[![npm](https://img.shields.io/npm/v/classic-level.svg)](https://www.npmjs.com/package/classic-level) +[![Node version](https://img.shields.io/node/v/classic-level.svg)](https://www.npmjs.com/package/classic-level) +[![Test](https://img.shields.io/github/workflow/status/Level/classic-level/Test?label=test)](https://github.com/Level/classic-level/actions/workflows/test.yml) +[![Coverage](https://img.shields.io/codecov/c/github/Level/classic-level?label=\&logo=codecov\&logoColor=fff)](https://codecov.io/gh/Level/classic-level) +[![Standard](https://img.shields.io/badge/standard-informational?logo=javascript\&logoColor=fff)](https://standardjs.com) +[![Common Changelog](https://common-changelog.org/badge.svg)](https://common-changelog.org) +[![Donate](https://img.shields.io/badge/donate-orange?logo=open-collective\&logoColor=fff)](https://opencollective.com/level) + +## Table of Contents + +
Click to expand + +- [Usage](#usage) +- [Supported Platforms](#supported-platforms) +- [API](#api) + - [`db = new ClassicLevel(location[, options])`](#db--new-classiclevellocation-options) + - [`db.location`](#dblocation) + - [`db.status`](#dbstatus) + - [`db.open([options][, callback])`](#dbopenoptions-callback) + - [`db.close([callback])`](#dbclosecallback) + - [`db.supports`](#dbsupports) + - [`db.get(key[, options][, callback])`](#dbgetkey-options-callback) + - [`db.getMany(keys[, options][, callback])`](#dbgetmanykeys-options-callback) + - [`db.put(key, value[, options][, callback])`](#dbputkey-value-options-callback) + - [`db.del(key[, options][, callback])`](#dbdelkey-options-callback) + - [`db.batch(operations[, options][, callback])`](#dbbatchoperations-options-callback) + - [`chainedBatch = db.batch()`](#chainedbatch--dbbatch) + - [`iterator = db.iterator([options])`](#iterator--dbiteratoroptions) + - [About high water](#about-high-water) + - [`keyIterator = db.keys([options])`](#keyiterator--dbkeysoptions) + - [`valueIterator = db.values([options])`](#valueiterator--dbvaluesoptions) + - [`db.clear([options][, callback])`](#dbclearoptions-callback) + - [`sublevel = db.sublevel(name[, options])`](#sublevel--dbsublevelname-options) + - [`db.approximateSize(start, end[, options][, callback])`](#dbapproximatesizestart-end-options-callback) + - [`db.compactRange(start, end[, options][, callback])`](#dbcompactrangestart-end-options-callback) + - [`db.getProperty(property)`](#dbgetpropertyproperty) + - [`chainedBatch`](#chainedbatch) + - [`chainedBatch.put(key, value[, options])`](#chainedbatchputkey-value-options) + - [`chainedBatch.del(key[, options])`](#chainedbatchdelkey-options) + - [`chainedBatch.clear()`](#chainedbatchclear) + - [`chainedBatch.write([options][, callback])`](#chainedbatchwriteoptions-callback) + - [`chainedBatch.close([callback])`](#chainedbatchclosecallback) + - [`chainedBatch.length`](#chainedbatchlength) + - [`chainedBatch.db`](#chainedbatchdb) + - [`iterator`](#iterator) + - [`for await...of iterator`](#for-awaitof-iterator) + - [`iterator.next([callback])`](#iteratornextcallback) + - [`iterator.nextv(size[, options][, callback])`](#iteratornextvsize-options-callback) + - [`iterator.all([options][, callback])`](#iteratoralloptions-callback) + - [`iterator.seek(target[, options])`](#iteratorseektarget-options) + - [`iterator.close([callback])`](#iteratorclosecallback) + - [`iterator.db`](#iteratordb) + - [`iterator.count`](#iteratorcount) + - [`iterator.limit`](#iteratorlimit) + - [`keyIterator`](#keyiterator) + - [`valueIterator`](#valueiterator) + - [`sublevel`](#sublevel) + - [`sublevel.prefix`](#sublevelprefix) + - [`sublevel.db`](#subleveldb) + - [`ClassicLevel.destroy(location[, callback])`](#classicleveldestroylocation-callback) + - [`ClassicLevel.repair(location[, callback])`](#classiclevelrepairlocation-callback) +- [Development](#development) + - [Getting Started](#getting-started) + - [Contributing](#contributing) + - [Publishing](#publishing) +- [Donate](#donate) +- [License](#license) + +
+ +## Usage + +```js +const { ClassicLevel } = require('classic-level') + +// Create a database +const db = new ClassicLevel('./db', { valueEncoding: 'json' }) + +// Add an entry with key 'a' and value 1 +await db.put('a', 1) + +// Add multiple entries +await db.batch([{ type: 'put', key: 'b', value: 2 }]) + +// Get value of key 'a': 1 +const value = await db.get('a') + +// Iterate entries with keys that are greater than 'a' +for await (const [key, value] of db.iterator({ gt: 'a' })) { + console.log(value) // 2 +} +``` + +All asynchronous methods also support callbacks. + +
Callback example + +```js +db.put('example', { hello: 'world' }, (err) => { + if (err) throw err + + db.get('example', (err, value) => { + if (err) throw err + console.log(value) // { hello: 'world' } + }) +}) +``` + +
+ +Usage from TypeScript requires generic type parameters. + +
TypeScript example + +```ts +// Specify types of keys and values (any, in the case of json). +// The generic type parameters default to ClassicLevel. +const db = new ClassicLevel('./db', { valueEncoding: 'json' }) + +// All relevant methods then use those types +await db.put('a', { x: 123 }) + +// Specify different types when overriding encoding per operation +await db.get('a', { valueEncoding: 'utf8' }) + +// Though in some cases TypeScript can infer them +await db.get('a', { valueEncoding: db.valueEncoding('utf8') }) + +// It works the same for sublevels +const abc = db.sublevel('abc') +const xyz = db.sublevel('xyz', { valueEncoding: 'json' }) +``` + +
+ +## Supported Platforms + +We aim to support _at least_ Active LTS and Current Node.js releases, Electron 5.0.0, as well as any future Node.js and Electron releases thanks to [Node-API](https://nodejs.org/api/n-api.html). + +The `classic-level` npm package ships with prebuilt binaries for popular 64-bit platforms as well as ARM, M1, Android, Alpine (musl), Windows 32-bit, Linux flavors with an old glibc (Debian 8, Ubuntu 14.04, RHEL 7, CentOS 7) and is known to work on: + +- **Linux**, including ARM platforms such as Raspberry Pi and Kindle +- **Mac OS** (10.7 and later) +- **Windows** +- **FreeBSD** + +When installing `classic-level`, [`node-gyp-build`](https://github.com/prebuild/node-gyp-build) will check if a compatible binary exists and fallback to compiling from source if it doesn't. In that case you'll need a [valid `node-gyp` installation](https://github.com/nodejs/node-gyp#installation). + +If you don't want to use the prebuilt binary for the platform you are installing on, specify the `--build-from-source` flag when you install. One of: + +``` +npm install --build-from-source +npm install classic-level --build-from-source +``` + +If you are working on `classic-level` itself and want to recompile the C++ code, run `npm run rebuild`. + +Note: the Android prebuilds are made for and built against Node.js core rather than the [`nodejs-mobile`](https://github.com/JaneaSystems/nodejs-mobile) fork. + +## API + +The API of `classic-level` follows that of [`abstract-level`](https://github.com/Level/abstract-level) with a few additional options and methods specific to LevelDB. The documentation below covers it all except for [Encodings](https://github.com/Level/abstract-level#encodings), [Events](https://github.com/Level/abstract-level#events) and [Errors](https://github.com/Level/abstract-level#errors) which are exclusively documented in `abstract-level`. + +An `abstract-level` and thus `classic-level` database is at its core a [key-value database](https://en.wikipedia.org/wiki/Key%E2%80%93value_database). A key-value pair is referred to as an _entry_ here and typically returned as an array, comparable to [`Object.entries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries). + +### `db = new ClassicLevel(location[, options])` + +Create a database or open an existing database. The `location` argument must be a directory path (relative or absolute) where LevelDB will store its files. If the directory does not yet exist (and `options.createIfMissing` is true) it will be created recursively. The optional `options` object may contain: + +- `keyEncoding` (string or object, default `'utf8'`): encoding to use for keys +- `valueEncoding` (string or object, default `'utf8'`): encoding to use for values. + +See [Encodings](https://github.com/Level/abstract-level#encodings) for a full description of these options. Other `options` (except `passive`) are forwarded to `db.open()` which is automatically called in a next tick after the constructor returns. Any read & write operations are queued internally until the database has finished opening. If opening fails, those queued operations will yield errors. + +### `db.location` + +Read-only getter that returns the `location` string that was passed to the constructor (as-is). + +### `db.status` + +Read-only getter that returns a string reflecting the current state of the database: + +- `'opening'` - waiting for the database to be opened +- `'open'` - successfully opened the database +- `'closing'` - waiting for the database to be closed +- `'closed'` - successfully closed the database. + +### `db.open([options][, callback])` + +Open the database. The `callback` function will be called with no arguments when successfully opened, or with a single error argument if opening failed. The database has an exclusive lock (on disk): if another process or instance has already opened the underlying LevelDB store at the given `location` then opening will fail with error code [`LEVEL_LOCKED`](https://github.com/Level/abstract-level#errors). If no callback is provided, a promise is returned. Options passed to `open()` take precedence over options passed to the database constructor. + +The optional `options` object may contain: + +- `createIfMissing` (boolean, default: `true`): If `true`, create an empty database if one doesn't already exist. If `false` and the database doesn't exist, opening will fail. +- `errorIfExists` (boolean, default: `false`): If `true` and the database already exists, opening will fail. +- `passive` (boolean, default: `false`): Wait for, but do not initiate, opening of the database. +- `multithreading` (boolean, default: `false`): Allow multiple threads to access the database. This is only relevant when using [worker threads](https://nodejs.org/api/worker_threads.html) + +For advanced performance tuning, the `options` object may also contain the following. Modify these options only if you can prove actual benefit for your particular application. + +
+Click to expand + +- `compression` (boolean, default: `true`): Unless set to `false`, all _compressible_ data will be run through the Snappy compression algorithm before being stored. Snappy is very fast so leave this on unless you have good reason to turn it off. + +- `cacheSize` (number, default: `8 * 1024 * 1024`): The size (in bytes) of the in-memory [LRU](http://en.wikipedia.org/wiki/Least_Recently_Used) cache with frequently used uncompressed block contents. + +- `writeBufferSize` (number, default: `4 * 1024 * 1024`): The maximum size (in bytes) of the log (in memory and stored in the `.log` file on disk). Beyond this size, LevelDB will convert the log data to the first level of sorted table files. From LevelDB documentation: + + > Larger values increase performance, especially during bulk loads. Up to two write buffers may be held in memory at the same time, so you may wish to adjust this parameter to control memory usage. Also, a larger write buffer will result in a longer recovery time the next time the database is opened. + +- `blockSize` (number, default: `4096`): The _approximate_ size of the blocks that make up the table files. The size relates to uncompressed data (hence "approximate"). Blocks are indexed in the table file and entry-lookups involve reading an entire block and parsing to discover the required entry. + +- `maxOpenFiles` (number, default: `1000`): The maximum number of files that LevelDB is allowed to have open at a time. If your database is likely to have a large working set, you may increase this value to prevent file descriptor churn. To calculate the number of files required for your working set, divide your total data size by `maxFileSize`. + +- `blockRestartInterval` (number, default: `16`): The number of entries before restarting the "delta encoding" of keys within blocks. Each "restart" point stores the full key for the entry, between restarts, the common prefix of the keys for those entries is omitted. Restarts are similar to the concept of keyframes in video encoding and are used to minimise the amount of space required to store keys. This is particularly helpful when using deep namespacing / prefixing in your keys. + +- `maxFileSize` (number, default: `2 * 1024 * 1024`): The maximum amount of bytes to write to a file before switching to a new one. From LevelDB documentation: + + > If your filesystem is more efficient with larger files, you could consider increasing the value. The downside will be longer compactions and hence longer latency / performance hiccups. Another reason to increase this parameter might be when you are initially populating a large database. + +
+ +It's generally not necessary to call `open()` because it's automatically called by the database constructor. It may however be useful to capture an error from failure to open, that would otherwise not surface until another method like `db.get()` is called. It's also possible to reopen the database after it has been closed with [`close()`](#dbclosecallback). Once `open()` has then been called, any read & write operations will again be queued internally until opening has finished. + +The `open()` and `close()` methods are idempotent. If the database is already open, the `callback` will be called in a next tick. If opening is already in progress, the `callback` will be called when that has finished. If closing is in progress, the database will be reopened once closing has finished. Likewise, if `close()` is called after `open()`, the database will be closed once opening has finished and the prior `open()` call will receive an error. + +### `db.close([callback])` + +Close the database. The `callback` function will be called with no arguments if closing succeeded or with a single `error` argument if closing failed. If no callback is provided, a promise is returned. + +A database has associated resources like file handles and locks. When the database is no longer needed (for the remainder of a program) it's recommended to call `db.close()` to free up resources. The underlying LevelDB store cannot be opened by multiple `classic-level` instances or processes simultaneously. + +After `db.close()` has been called, no further read & write operations are allowed unless and until `db.open()` is called again. For example, `db.get(key)` will yield an error with code [`LEVEL_DATABASE_NOT_OPEN`](https://github.com/Level/abstract-level#errors). Any unclosed iterators or chained batches will be closed by `db.close()` and can then no longer be used even when `db.open()` is called again. + +A `classic-level` database waits for any pending operations to finish before closing. For example: + +```js +db.put('key', 'value', function (err) { + // This happens first +}) + +db.close(function (err) { + // This happens second +}) +``` + +### `db.supports` + +A [manifest](https://github.com/Level/supports) describing the features supported by this database. Might be used like so: + +```js +if (!db.supports.permanence) { + throw new Error('Persistent storage is required') +} +``` + +### `db.get(key[, options][, callback])` + +Get a value from the database by `key`. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. +- `valueEncoding`: custom value encoding for this operation, used to decode the value. +- `fillCache` (boolean, default: `true`): Unless set to `false`, LevelDB will fill its in-memory [LRU](http://en.wikipedia.org/wiki/Least_Recently_Used) cache with data that was read. + +The `callback` function will be called with an error if the operation failed. If the key was not found, the error will have code [`LEVEL_NOT_FOUND`](https://github.com/Level/abstract-level#errors). If successful the first argument will be `null` and the second argument will be the value. If no callback is provided, a promise is returned. + +A `classic-level` database supports snapshots (as indicated by `db.supports.snapshots`) which means `db.get()` _should_ read from a snapshot of the database, created at the time `db.get()` was called. This means it should not see the data of simultaneous write operations. However, there's currently a small delay before the snapshot is created. + +### `db.getMany(keys[, options][, callback])` + +Get multiple values from the database by an array of `keys`. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `keys`. +- `valueEncoding`: custom value encoding for this operation, used to decode values. +- `fillCache`: same as described for [`db.get()`](#dbgetkey-options-callback). + +The `callback` function will be called with an error if the operation failed. If successful the first argument will be `null` and the second argument will be an array of values with the same order as `keys`. If a key was not found, the relevant value will be `undefined`. If no callback is provided, a promise is returned. + +A `classic-level` database supports snapshots (as indicated by `db.supports.snapshots`) which means `db.getMany()` _should_ read from a snapshot of the database, created at the time `db.getMany()` was called. This means it should not see the data of simultaneous write operations. However, there's currently a small delay before the snapshot is created. + +### `db.put(key, value[, options][, callback])` + +Add a new entry or overwrite an existing entry. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. +- `valueEncoding`: custom value encoding for this operation, used to encode the `value`. +- `sync` (boolean, default: `false`): if set to `true`, LevelDB will perform a synchronous write of the data although the operation will be asynchronous as far as Node.js or Electron is concerned. Normally, LevelDB passes the data to the operating system for writing and returns immediately. In contrast, a synchronous write will use [`fsync()`](https://man7.org/linux/man-pages/man2/fsync.2.html) or equivalent, so the `put()` call will not complete until the data is actually on disk. Synchronous writes are significantly slower than asynchronous writes. + +The `callback` function will be called with no arguments if the operation was successful or with an error if it failed. If no callback is provided, a promise is returned. + +### `db.del(key[, options][, callback])` + +Delete an entry by `key`. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. +- `sync` (boolean, default: `false`): same as described for [`db.put()`](#dbputkey-value-options-callback) + +The `callback` function will be called with no arguments if the operation was successful or with an error if it failed. If no callback is provided, a promise is returned. + +### `db.batch(operations[, options][, callback])` + +Perform multiple _put_ and/or _del_ operations in bulk. The `operations` argument must be an array containing a list of operations to be executed sequentially, although as a whole they are performed as an atomic operation. + +Each operation must be an object with at least a `type` property set to either `'put'` or `'del'`. If the `type` is `'put'`, the operation must have `key` and `value` properties. It may optionally have `keyEncoding` and / or `valueEncoding` properties to encode keys or values with a custom encoding for just that operation. If the `type` is `'del'`, the operation must have a `key` property and may optionally have a `keyEncoding` property. + +An operation of either type may also have a `sublevel` property, to prefix the key of the operation with the prefix of that sublevel. This allows atomically committing data to multiple sublevels. Keys and values will be encoded by the sublevel, to the same effect as a `sublevel.batch(..)` call. In the following example, the first `value` will be encoded with `'json'` rather than the default encoding of `db`: + +```js +const people = db.sublevel('people', { valueEncoding: 'json' }) +const nameIndex = db.sublevel('names') + +await db.batch([{ + type: 'put', + sublevel: people, + key: '123', + value: { + name: 'Alice' + } +}, { + type: 'put', + sublevel: nameIndex, + key: 'Alice', + value: '123' +}]) +``` + +The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this batch, used to encode keys. +- `valueEncoding`: custom value encoding for this batch, used to encode values. +- `sync` (boolean, default: `false`): same as described for [`db.put()`](#dbputkey-value-options-callback). + +Encoding properties on individual operations take precedence. In the following example, the first value will be encoded with the `'utf8'` encoding and the second with `'json'`. + +```js +await db.batch([ + { type: 'put', key: 'a', value: 'foo' }, + { type: 'put', key: 'b', value: 123, valueEncoding: 'json' } +], { valueEncoding: 'utf8' }) +``` + +The `callback` function will be called with no arguments if the batch was successful or with an error if it failed. If no callback is provided, a promise is returned. + +### `chainedBatch = db.batch()` + +Create a [`chained batch`](#chainedbatch), when `batch()` is called with zero arguments. A chained batch can be used to build and eventually commit an atomic batch of operations. Depending on how it's used, it is possible to obtain greater performance with this form of `batch()`. + +```js +await db.batch() + .del('bob') + .put('alice', 361) + .put('kim', 220) + .write() +``` + +### `iterator = db.iterator([options])` + +Create an [`iterator`](#iterator). The optional `options` object may contain the following _range options_ to control the range of entries to be iterated: + +- `gt` (greater than) or `gte` (greater than or equal): define the lower bound of the range to be iterated. Only entries where the key is greater than (or equal to) this option will be included in the range. When `reverse` is true the order will be reversed, but the entries iterated will be the same. +- `lt` (less than) or `lte` (less than or equal): define the higher bound of the range to be iterated. Only entries where the key is less than (or equal to) this option will be included in the range. When `reverse` is true the order will be reversed, but the entries iterated will be the same. +- `reverse` (boolean, default: `false`): iterate entries in reverse order. Beware that a reverse seek can be slower than a forward seek. +- `limit` (number, default: `Infinity`): limit the number of entries yielded. This number represents a _maximum_ number of entries and will not be reached if the end of the range is reached first. A value of `Infinity` or `-1` means there is no limit. When `reverse` is true the entries with the highest keys will be returned instead of the lowest keys. + +The `gte` and `lte` range options take precedence over `gt` and `lt` respectively. If no range options are provided, the iterator will visit all entries of the database, starting at the lowest key and ending at the highest key (unless `reverse` is true). In addition to range options, the `options` object may contain: + +- `keys` (boolean, default: `true`): whether to return the key of each entry. If set to `false`, the iterator will yield keys that are `undefined`. Prefer to use `db.keys()` instead. +- `values` (boolean, default: `true`): whether to return the value of each entry. If set to `false`, the iterator will yield values that are `undefined`. Prefer to use `db.values()` instead. +- `keyEncoding`: custom key encoding for this iterator, used to encode range options, to encode `seek()` targets and to decode keys. +- `valueEncoding`: custom value encoding for this iterator, used to decode values. +- `fillCache` (boolean, default: `false`): if set to `true`, LevelDB will fill its in-memory [LRU](http://en.wikipedia.org/wiki/Least_Recently_Used) cache with data that was read. +- `highWaterMarkBytes` (number, default: `16 * 1024`): limit the amount of data that the iterator will hold in memory. Explained below. + +#### About high water + +While [`iterator.nextv(size)`](#iteratornextvsize-options-callback) is reading entries from LevelDB into memory, it sums up the byte length of those entries. If and when that sum has exceeded `highWaterMarkBytes`, reading will stop. If `nextv(2)` would normally yield two entries but the first entry is too large, then only one entry will be yielded. More `nextv(size)` calls must then be made to get the remaining entries. + +If memory usage is less of a concern, increasing `highWaterMarkBytes` can increase the throughput of `nextv(size)`. If set to `0` then `nextv(size)` will never yield more than one entry, as `highWaterMarkBytes` will be exceeded on each call. It can not be set to `Infinity`. On key- and value iterators (see below) it applies to the byte length of keys or values respectively, rather than the combined byte length of keys _and_ values. + +Optimal performance can be achieved by setting `highWaterMarkBytes` to at least `size` multiplied by the expected byte length of an entry, ensuring that `size` is always met. In other words, that `nextv(size)` will not stop reading before `size` amount of entries have been read into memory. If the iterator is wrapped in a [Node.js stream](https://github.com/Level/read-stream) or [Web Stream](https://github.com/Level/web-stream) then the `size` parameter is dictated by the stream's `highWaterMark` option. For example: + +```js +const { EntryStream } = require('level-read-stream') + +// If an entry is 50 bytes on average +const stream = new EntryStream(db, { + highWaterMark: 1000, + highWaterMarkBytes: 1000 * 50 +}) +``` + +Side note: the "watermark" analogy makes more sense in Node.js streams because its internal `highWaterMark` can grow, indicating the highest that the "water" has been. In a `classic-level` iterator however, `highWaterMarkBytes` is fixed once set. Getting exceeded does not change it. + +The `highWaterMarkBytes` option is also applied to an internal cache that `classic-level` employs for [`next()`](#iteratornextcallback) and [`for await...of`](#for-awaitof-iterator). When `next()` is called, that cache is populated with at most 1000 entries, or less than that if `highWaterMarkBytes` is exceeded by the total byte length of entries. To avoid reading too eagerly, the cache is not populated on the first `next()` call, or the first `next()` call after a `seek()`. Only on subsequent `next()` calls. + +### `keyIterator = db.keys([options])` + +Create a [key iterator](#keyiterator), having the same interface as `db.iterator()` except that it yields keys instead of entries. If only keys are needed, using `db.keys()` may increase performance because values won't have to fetched, copied or decoded. Options are the same as for `db.iterator()` except that `db.keys()` does not take `keys`, `values` and `valueEncoding` options. + +```js +// Iterate lazily +for await (const key of db.keys({ gt: 'a' })) { + console.log(key) +} + +// Get all at once. Setting a limit is recommended. +const keys = await db.keys({ gt: 'a', limit: 10 }).all() +``` + +### `valueIterator = db.values([options])` + +Create a [value iterator](#valueiterator), having the same interface as `db.iterator()` except that it yields values instead of entries. If only values are needed, using `db.values()` may increase performance because keys won't have to fetched, copied or decoded. Options are the same as for `db.iterator()` except that `db.values()` does not take `keys` and `values` options. Note that it _does_ take a `keyEncoding` option, relevant for the encoding of range options. + +```js +// Iterate lazily +for await (const value of db.values({ gt: 'a' })) { + console.log(value) +} + +// Get all at once. Setting a limit is recommended. +const values = await db.values({ gt: 'a', limit: 10 }).all() +``` + +### `db.clear([options][, callback])` + +Delete all entries or a range. Not guaranteed to be atomic. Accepts the following options (with the same rules as on iterators): + +- `gt` (greater than) or `gte` (greater than or equal): define the lower bound of the range to be deleted. Only entries where the key is greater than (or equal to) this option will be included in the range. When `reverse` is true the order will be reversed, but the entries deleted will be the same. +- `lt` (less than) or `lte` (less than or equal): define the higher bound of the range to be deleted. Only entries where the key is less than (or equal to) this option will be included in the range. When `reverse` is true the order will be reversed, but the entries deleted will be the same. +- `reverse` (boolean, default: `false`): delete entries in reverse order. Only effective in combination with `limit`, to delete the last N entries. +- `limit` (number, default: `Infinity`): limit the number of entries to be deleted. This number represents a _maximum_ number of entries and will not be reached if the end of the range is reached first. A value of `Infinity` or `-1` means there is no limit. When `reverse` is true the entries with the highest keys will be deleted instead of the lowest keys. +- `keyEncoding`: custom key encoding for this operation, used to encode range options. + +The `gte` and `lte` range options take precedence over `gt` and `lt` respectively. If no options are provided, all entries will be deleted. The `callback` function will be called with no arguments if the operation was successful or with an error if it failed. If no callback is provided, a promise is returned. + +### `sublevel = db.sublevel(name[, options])` + +Create a [sublevel](#sublevel) that has the same interface as `db` (except for additional `classic-level` methods like `db.approximateSize()`) and prefixes the keys of operations before passing them on to `db`. The `name` argument is required and must be a string. + +```js +const example = db.sublevel('example') + +await example.put('hello', 'world') +await db.put('a', '1') + +// Prints ['hello', 'world'] +for await (const [key, value] of example.iterator()) { + console.log([key, value]) +} +``` + +Sublevels effectively separate a database into sections. Think SQL tables, but evented, ranged and realtime! Each sublevel is an `AbstractLevel` instance with its own keyspace, [events](https://github.com/Level/abstract-level#events) and [encodings](https://github.com/Level/abstract-level#encodings). For example, it's possible to have one sublevel with `'buffer'` keys and another with `'utf8'` keys. The same goes for values. Like so: + +```js +db.sublevel('one', { valueEncoding: 'json' }) +db.sublevel('two', { keyEncoding: 'buffer' }) +``` + +An own keyspace means that `sublevel.iterator()` only includes entries of that sublevel, `sublevel.clear()` will only delete entries of that sublevel, and so forth. Range options get prefixed too. + +Fully qualified keys (as seen from the parent database) take the form of `prefix + key` where `prefix` is `separator + name + separator`. If `name` is empty, the effective prefix is two separators. Sublevels can be nested: if `db` is itself a sublevel then the effective prefix is a combined prefix, e.g. `'!one!!two!'`. Note that a parent database will see its own keys as well as keys of any nested sublevels: + +```js +// Prints ['!example!hello', 'world'] and ['a', '1'] +for await (const [key, value] of db.iterator()) { + console.log([key, value]) +} +``` + +> :pushpin: The key structure is equal to that of [`subleveldown`](https://github.com/Level/subleveldown) which offered sublevels before they were built-in to `abstract-level` and thus `classic-level`. This means that an `classic-level` sublevel can read sublevels previously created with (and populated by) `subleveldown`. + +Internally, sublevels operate on keys that are either a string, Buffer or Uint8Array, depending on choice of encoding. Which is to say: binary keys are fully supported. The `name` must however always be a string and can only contain ASCII characters. + +The optional `options` object may contain: + +- `separator` (string, default: `'!'`): Character for separating sublevel names from user keys and each other. Must sort before characters used in `name`. An error will be thrown if that's not the case. +- `keyEncoding` (string or object, default `'utf8'`): encoding to use for keys +- `valueEncoding` (string or object, default `'utf8'`): encoding to use for values. + +The `keyEncoding` and `valueEncoding` options are forwarded to the `AbstractLevel` constructor and work the same, as if a new, separate database was created. They default to `'utf8'` regardless of the encodings configured on `db`. Other options are forwarded too but `classic-level` has no relevant options at the time of writing. For example, setting the `createIfMissing` option will have no effect. Why is that? + +Like regular databases, sublevels open themselves but they do not affect the state of the parent database. This means a sublevel can be individually closed and (re)opened. If the sublevel is created while the parent database is opening, it will wait for that to finish. If the parent database is closed, then opening the sublevel will fail and subsequent operations on the sublevel will yield errors with code [`LEVEL_DATABASE_NOT_OPEN`](https://github.com/Level/abstract-level#errors). + +### `db.approximateSize(start, end[, options][, callback])` + +Get the approximate number of bytes of file system space used by the range `[start..end)`. The result might not include recently written data. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode `start` and `end`. + +The `callback` function will be called with a single error argument if the operation failed. If successful the first argument will be `null` and the second argument will be the approximate size as a number. If no callback is provided, a promise is returned. This method is an additional method that is not part of the [`abstract-level`](https://github.com/Level/abstract-level) interface. + +### `db.compactRange(start, end[, options][, callback])` + +Manually trigger a database compaction in the range `[start..end]`. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode `start` and `end`. + +The `callback` function will be called with no arguments if the operation was successful or with an error if it failed. If no callback is provided, a promise is returned. This method is an additional method that is not part of the [`abstract-level`](https://github.com/Level/abstract-level) interface. + +### `db.getProperty(property)` + +Get internal details from LevelDB. When issued with a valid `property` string, a string value is returned synchronously. Valid properties are: + +- `leveldb.num-files-at-levelN`: return the number of files at level _N_, where N is an integer representing a valid level (e.g. "0"). +- `leveldb.stats`: returns a multi-line string describing statistics about LevelDB's internal operation. +- `leveldb.sstables`: returns a multi-line string describing all of the _sstables_ that make up contents of the current database. + +This method is an additional method that is not part of the [`abstract-level`](https://github.com/Level/abstract-level) interface. + +### `chainedBatch` + +#### `chainedBatch.put(key, value[, options])` + +Queue a `put` operation on this batch, not committed until `write()` is called. This will throw a [`LEVEL_INVALID_KEY`](https://github.com/Level/abstract-level#errors) or [`LEVEL_INVALID_VALUE`](https://github.com/Level/abstract-level#errors) error if `key` or `value` is invalid. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. +- `valueEncoding`: custom value encoding for this operation, used to encode the `value`. +- `sublevel` (sublevel instance): act as though the `put` operation is performed on the given sublevel, to similar effect as `sublevel.batch().put(key, value)`. This allows atomically committing data to multiple sublevels. The `key` will be prefixed with the `prefix` of the sublevel, and the `key` and `value` will be encoded by the sublevel (using the default encodings of the sublevel unless `keyEncoding` and / or `valueEncoding` are provided). + +#### `chainedBatch.del(key[, options])` + +Queue a `del` operation on this batch, not committed until `write()` is called. This will throw a [`LEVEL_INVALID_KEY`](https://github.com/Level/abstract-level#errors) error if `key` is invalid. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. +- `sublevel` (sublevel instance): act as though the `del` operation is performed on the given sublevel, to similar effect as `sublevel.batch().del(key)`. This allows atomically committing data to multiple sublevels. The `key` will be prefixed with the `prefix` of the sublevel, and the `key` will be encoded by the sublevel (using the default key encoding of the sublevel unless `keyEncoding` is provided). + +#### `chainedBatch.clear()` + +Clear all queued operations on this batch. + +#### `chainedBatch.write([options][, callback])` + +Commit the queued operations for this batch. All operations will be written atomically, that is, they will either all succeed or fail with no partial commits. + +The optional `options` object may contain: + +- `sync` (boolean, default: `false`): same as described for [`db.put()`](#dbputkey-value-options-callback). + +Note that `write()` does not take encoding options. Those can only be set on `put()` and `del()` because `classic-level` synchronously forwards such calls to LevelDB and thus need keys and values to be encoded at that point. + +The `callback` function will be called with no arguments if the batch was successful or with an error if it failed. If no callback is provided, a promise is returned. + +After `write()` or `close()` has been called, no further operations are allowed. + +#### `chainedBatch.close([callback])` + +Free up underlying resources. This should be done even if the chained batch has zero queued operations. Automatically called by `write()` so normally not necessary to call, unless the intent is to discard a chained batch without committing it. The `callback` function will be called with no arguments. If no callback is provided, a promise is returned. Closing the batch is an idempotent operation, such that calling `close()` more than once is allowed and makes no difference. + +#### `chainedBatch.length` + +The number of queued operations on the current batch. + +#### `chainedBatch.db` + +A reference to the database that created this chained batch. + +### `iterator` + +An iterator allows one to lazily read a range of entries stored in the database. The entries will be sorted by keys in [lexicographic order](https://en.wikipedia.org/wiki/Lexicographic_order) (in other words: byte order) which in short means key `'a'` comes before `'b'` and key `'10'` comes before `'2'`. + +An iterator reads from a snapshot of the database, created at the time `db.iterator()` was called. This means the iterator will not see the data of simultaneous write operations. + +Iterators can be consumed with [`for await...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) and `iterator.all()`, or by manually calling `iterator.next()` or `nextv()` in succession. In the latter case, `iterator.close()` must always be called. In contrast, finishing, throwing, breaking or returning from a `for await...of` loop automatically calls `iterator.close()`, as does `iterator.all()`. + +An iterator reaches its natural end in the following situations: + +- The end of the database has been reached +- The end of the range has been reached +- The last `iterator.seek()` was out of range. + +An iterator keeps track of calls that are in progress. It doesn't allow concurrent `next()`, `nextv()` or `all()` calls (including a combination thereof) and will throw an error with code [`LEVEL_ITERATOR_BUSY`](https://github.com/Level/abstract-level#errors) if that happens: + +```js +// Not awaited and no callback provided +iterator.next() + +try { + // Which means next() is still in progress here + iterator.all() +} catch (err) { + console.log(err.code) // 'LEVEL_ITERATOR_BUSY' +} +``` + +#### `for await...of iterator` + +Yields entries, which are arrays containing a `key` and `value`. The type of `key` and `value` depends on the options passed to `db.iterator()`. + +```js +try { + for await (const [key, value] of db.iterator()) { + console.log(key) + } +} catch (err) { + console.error(err) +} +``` + +#### `iterator.next([callback])` + +Advance to the next entry and yield that entry. If an error occurs, the `callback` function will be called with an error. Otherwise, the `callback` receives `null`, a `key` and a `value`. The type of `key` and `value` depends on the options passed to `db.iterator()`. If the iterator has reached its natural end, both `key` and `value` will be `undefined`. + +If no callback is provided, a promise is returned for either an array (containing a `key` and `value`) or `undefined` if the iterator reached its natural end. + +**Note:** `iterator.close()` must always be called once there's no intention to call `next()` or `nextv()` again. Even if such calls yielded an error and even if the iterator reached its natural end. Not closing the iterator will result in memory leaks and may also affect performance of other operations if many iterators are unclosed and each is holding a snapshot of the database. + +#### `iterator.nextv(size[, options][, callback])` + +Advance repeatedly and get at most `size` amount of entries in a single call. Can be faster than repeated `next()` calls. The `size` argument must be an integer and has a soft minimum of 1. There are no `options` currently. + +If an error occurs, the `callback` function will be called with an error. Otherwise, the `callback` receives `null` and an array of entries, where each entry is an array containing a key and value. The natural end of the iterator will be signaled by yielding an empty array. If no callback is provided, a promise is returned. + +```js +const iterator = db.iterator() + +while (true) { + const entries = await iterator.nextv(100) + + if (entries.length === 0) { + break + } + + for (const [key, value] of entries) { + // .. + } +} + +await iterator.close() +``` + +#### `iterator.all([options][, callback])` + +Advance repeatedly and get all (remaining) entries as an array, automatically closing the iterator. Assumes that those entries fit in memory. If that's not the case, instead use `next()`, `nextv()` or `for await...of`. There are no `options` currently. If an error occurs, the `callback` function will be called with an error. Otherwise, the `callback` receives `null` and an array of entries, where each entry is an array containing a key and value. If no callback is provided, a promise is returned. + +```js +const entries = await db.iterator({ limit: 100 }).all() + +for (const [key, value] of entries) { + // .. +} +``` + +#### `iterator.seek(target[, options])` + +Seek to the key closest to `target`. Subsequent calls to `iterator.next()`, `nextv()` or `all()` (including implicit calls in a `for await...of` loop) will yield entries with keys equal to or larger than `target`, or equal to or smaller than `target` if the `reverse` option passed to `db.iterator()` was true. + +The optional `options` object may contain: + +- `keyEncoding`: custom key encoding, used to encode the `target`. By default the `keyEncoding` option of the iterator is used or (if that wasn't set) the `keyEncoding` of the database. + +If range options like `gt` were passed to `db.iterator()` and `target` does not fall within that range, the iterator will reach its natural end. + +#### `iterator.close([callback])` + +Free up underlying resources. The `callback` function will be called with no arguments. If no callback is provided, a promise is returned. Closing the iterator is an idempotent operation, such that calling `close()` more than once is allowed and makes no difference. + +If a `next()` ,`nextv()` or `all()` call is in progress, closing will wait for that to finish. After `close()` has been called, further calls to `next()` ,`nextv()` or `all()` will yield an error with code [`LEVEL_ITERATOR_NOT_OPEN`](https://github.com/Level/abstract-level#errors). + +#### `iterator.db` + +A reference to the database that created this iterator. + +#### `iterator.count` + +Read-only getter that indicates how many keys have been yielded so far (by any method) excluding calls that errored or yielded `undefined`. + +#### `iterator.limit` + +Read-only getter that reflects the `limit` that was set in options. Greater than or equal to zero. Equals `Infinity` if no limit, which allows for easy math: + +```js +const hasMore = iterator.count < iterator.limit +const remaining = iterator.limit - iterator.count +``` + +### `keyIterator` + +A key iterator has the same interface as `iterator` except that its methods yield keys instead of entries. For the `keyIterator.next(callback)` method, this means that the `callback` will receive two arguments (an error and key) instead of three. Usage is otherwise the same. + +### `valueIterator` + +A value iterator has the same interface as `iterator` except that its methods yield values instead of entries. For the `valueIterator.next(callback)` method, this means that the `callback` will receive two arguments (an error and value) instead of three. Usage is otherwise the same. + +### `sublevel` + +A sublevel is an instance of the `AbstractSublevel` class (as found in [`abstract-level`](https://github.com/Level/abstract-level)) which extends `AbstractLevel` and thus has the same API as documented above, except for additional `classic-level` methods like `db.approximateSize()`. Sublevels have a few additional properties. + +#### `sublevel.prefix` + +Prefix of the sublevel. A read-only string property. + +```js +const example = db.sublevel('example') +const nested = example.sublevel('nested') + +console.log(example.prefix) // '!example!' +console.log(nested.prefix) // '!example!!nested!' +``` + +#### `sublevel.db` + +Parent database. A read-only property. + +```js +const example = db.sublevel('example') +const nested = example.sublevel('nested') + +console.log(example.db === db) // true +console.log(nested.db === db) // true +``` + +### `ClassicLevel.destroy(location[, callback])` + +Completely remove an existing LevelDB database directory. You can use this method in place of a full directory removal if you want to be sure to only remove LevelDB-related files. If the directory only contains LevelDB files, the directory itself will be removed as well. If there are additional, non-LevelDB files in the directory, those files and the directory will be left alone. + +The `callback` function will be called when the destroy operation is complete, with a possible error argument. If no callback is provided, a promise is returned. This method is an additional method that is not part of the [`abstract-level`](https://github.com/Level/abstract-level) interface. + +Before calling `destroy()`, close a database if it's using the same `location`: + +```js +const db = new ClassicLevel('./db') +await db.close() +await ClassicLevel.destroy('./db') +``` + +### `ClassicLevel.repair(location[, callback])` + +Attempt a restoration of a damaged database. It can also be used to perform a compaction of the LevelDB log into table files. From LevelDB documentation: + +> If a DB cannot be opened, you may attempt to call this method to resurrect as much of the contents of the database as possible. Some data may be lost, so be careful when calling this function on a database that contains important information. + +The `callback` function will be called when the repair operation is complete, with a possible error argument. If no callback is provided, a promise is returned. This method is an additional method that is not part of the [`abstract-level`](https://github.com/Level/abstract-level) interface. + +You will find information on the repair operation in the `LOG` file inside the database directory. + +Before calling `repair()`, close a database if it's using the same `location`. + +## Development + +### Getting Started + +This repository uses git submodules. Clone it recursively: + +```bash +git clone --recurse-submodules https://github.com/Level/classic-level.git +``` + +Alternatively, initialize submodules inside the working tree: + +```bash +cd classic-level +git submodule update --init --recursive +``` + +### Contributing + +[`Level/classic-level`](https://github.com/Level/classic-level) is an **OPEN Open Source Project**. This means that: + +> Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project. + +See the [Contribution Guide](https://github.com/Level/community/blob/master/CONTRIBUTING.md) for more details. + +### Publishing + +1. Increment the version: `npm version ..` +2. Push to GitHub: `git push --follow-tags` +3. Wait for CI to complete +4. Download prebuilds into `./prebuilds`: `npm run download-prebuilds` +5. Optionally verify loading a prebuild: `npm run test-prebuild` +6. Optionally verify which files npm will include: `canadian-pub` +7. Finally: `npm publish` + +## Donate + +Support us with a monthly donation on [Open Collective](https://opencollective.com/level) and help us continue our work. + +## License + +[MIT](LICENSE) + +[level-badge]: https://leveljs.org/img/badge.svg diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/UPGRADING.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/UPGRADING.md new file mode 100644 index 0000000..c1d74bf --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/UPGRADING.md @@ -0,0 +1,123 @@ +# Upgrade Guide + +This document describes breaking changes and how to upgrade. For a complete list of changes including minor and patch releases, please refer to the [changelog](CHANGELOG.md). + +## 1.0.0 + +**Introducing `classic-level`: a fork of [`leveldown`](https://github.com/Level/leveldown) that implements the [`abstract-level`](https://github.com/Level/abstract-level) interface instead of [`abstract-leveldown`](https://github.com/Level/abstract-leveldown). It thus has the same API as `level` and `levelup` including encodings, promises and events. In addition, you can now choose to use Uint8Array instead of Buffer. Sublevels are builtin.** + +We've put together several upgrade guides for different modules. See the [FAQ](https://github.com/Level/community#faq) to find the best upgrade guide for you. This one describes how to replace `leveldown` with `classic-level`. There will be a separate guide for upgrading `level`. + +Support of Node.js 10 has been dropped. + +### What is not covered here + +If you are using any of the following, please also read the upgrade guide of [`abstract-level@1`](https://github.com/Level/abstract-level/blob/main/UPGRADING.md#100) which goes into more detail about these: + +- Specific error messages (replaced with error codes) +- The `db.iterator().end()` method (renamed to `close()`, with `end()` as an alias) +- Zero-length keys and range options (now valid) +- The `db.supports.bufferKeys` property. + +### Changes to initialization + +We started using classes, which means using `new` is now required. If you previously did: + +```js +const leveldown = require('leveldown') +const db = leveldown('./db') +``` + +You must now do: + +```js +const { ClassicLevel } = require('classic-level') +const db = new ClassicLevel('./db') +``` + +Because `abstract-level` does not require calling `db.open()` before other methods (a feature known as deferred open) it is now preferred to pass options to the constructor. For example: + +```js +const db = new ClassicLevel('./db', { + createIfMissing: false, + compression: false +}) +``` + +If `db.open(options)` is called manually those options will be shallowly merged with options from the constructor: + +```js +// Results in { createIfMissing: false, compression: true } +await db.open({ compression: true }) +``` + +This means that if you were using this `db.open(options)` pattern, it works as before, except if you were _also_ wrapping `leveldown` with `levelup` _and_ passing options to the `levelup` constructor. Because `levelup` would overwrite rather than merge options. + +### There is only encodings + +The `asBuffer`, `valueAsBuffer` and `keyAsBuffer` options have been replaced with encoding options. The default encoding is `'utf8'` which means operations return strings rather than Buffers by default. If you previously did: + +```js +db.get('example', { asBuffer: false }, callback) +db.get('example', callback) +``` + +You must now do: + +```js +db.get('example', callback) +db.get('example', { valueEncoding: 'buffer' }, callback) +``` + +Or using promises (new): + +```js +const str = await db.get('example') +const buf = await db.get('example', { valueEncoding: 'buffer' }) +``` + +Or using Uint8Array (new): + +```js +const arr = await db.get('example', { valueEncoding: 'view' }) +``` + +### Unwrapping the onion + +If you were wrapping `leveldown` with `levelup`, `encoding-down` and / or `subleveldown`, remove those modules. If you previously did: + +```js +const leveldown = require('leveldown') +const levelup = require('levelup') +const enc = require('encoding-down') +const subleveldown = require('subleveldown') + +const db = levelup(enc(leveldown('./db'))) +const sublevel = subleveldown(db, 'foo') +``` + +You must now do: + +```js +const { ClassicLevel } = require('classic-level') +const db = new ClassicLevel('./db') +const sublevel = db.sublevel('foo') +``` + +### Changes to iterators + +- The `highWaterMark` option has been renamed to `highWaterMarkBytes` to remove a conflict with streams. Please see the README for details on this (previously undocumented) option. +- On iterators with `{ keys: false }` or `{ values: false }` options, the key or value is now `undefined` rather than an empty string (this was only the case in `leveldown`). +- The `iterator.cache` and `iterator.finished` properties are no longer accessible. If you were using these then you'll want to checkout the new `nextv()` method in the README. + +### Sugar on additional methods + +The additional methods `db.approximateSize()` and `db.compactRange()` now support the same patterns as other methods: + +- Support of encoding options +- Deferred open (no need to call `db.open()` before these methods) +- Returning a promise if no callback is provided. + +--- + +_For earlier releases, before `classic-level` was forked from `leveldown` (v6.1.0), please see [the upgrade guide of `leveldown`](https://github.com/Level/leveldown/blob/HEAD/UPGRADING.md)._ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/binding.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/binding.cc new file mode 100644 index 0000000..015b283 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/binding.cc @@ -0,0 +1,2170 @@ +#define NAPI_VERSION 3 + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +/** + * Forward declarations. + */ +struct Database; +struct Iterator; +static void iterator_close_do (napi_env env, Iterator* iterator, napi_value cb); +static leveldb::Status threadsafe_open(const leveldb::Options &options, + bool multithreading, + Database &db_instance); +static leveldb::Status threadsafe_close(Database &db_instance); + +/** + * Global declarations for multi-threaded access. These are not context-aware + * by definition and is specifically to allow for cross thread access to the + * single database handle. + */ +struct LevelDbHandle +{ + leveldb::DB *db; + size_t open_handle_count; +}; +static std::mutex handles_mutex; +// only access this when protected by the handles_mutex! +static std::map db_handles; + +/** + * Macros. + */ +#define NAPI_DB_CONTEXT() \ + Database* database = NULL; \ + NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database)); + +#define NAPI_ITERATOR_CONTEXT() \ + Iterator* iterator = NULL; \ + NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&iterator)); + +#define NAPI_BATCH_CONTEXT() \ + Batch* batch = NULL; \ + NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&batch)); + +#define NAPI_RETURN_UNDEFINED() \ + return 0; + +#define NAPI_UTF8_NEW(name, val) \ + size_t name##_size = 0; \ + NAPI_STATUS_THROWS(napi_get_value_string_utf8(env, val, NULL, 0, &name##_size)) \ + char* name = new char[name##_size + 1]; \ + NAPI_STATUS_THROWS(napi_get_value_string_utf8(env, val, name, name##_size + 1, &name##_size)) \ + name[name##_size] = '\0'; + +#define NAPI_ARGV_UTF8_NEW(name, i) \ + NAPI_UTF8_NEW(name, argv[i]) + +// TODO: consider using encoding options instead of type checking +#define LD_STRING_OR_BUFFER_TO_COPY(env, from, to) \ + char* to##Ch_ = 0; \ + size_t to##Sz_ = 0; \ + if (IsString(env, from)) { \ + napi_get_value_string_utf8(env, from, NULL, 0, &to##Sz_); \ + to##Ch_ = new char[to##Sz_ + 1]; \ + napi_get_value_string_utf8(env, from, to##Ch_, to##Sz_ + 1, &to##Sz_); \ + to##Ch_[to##Sz_] = '\0'; \ + } else if (IsBuffer(env, from)) { \ + char* buf = 0; \ + napi_get_buffer_info(env, from, (void **)&buf, &to##Sz_); \ + to##Ch_ = new char[to##Sz_]; \ + memcpy(to##Ch_, buf, to##Sz_); \ + } else { \ + char* buf = 0; \ + napi_typedarray_type type; \ + napi_status status = napi_get_typedarray_info(env, from, &type, &to##Sz_, (void **)&buf, NULL, NULL); \ + if (status != napi_ok || type != napi_typedarray_type::napi_uint8_array) { \ + /* TODO: refactor so that we can napi_throw_type_error() here */ \ + to##Sz_ = 0; \ + to##Ch_ = new char[to##Sz_]; \ + } else { \ + to##Ch_ = new char[to##Sz_]; \ + memcpy(to##Ch_, buf, to##Sz_); \ + } \ + } + +/********************************************************************* + * Helpers. + ********************************************************************/ + +/** + * Returns true if 'value' is a string. + */ +static bool IsString (napi_env env, napi_value value) { + napi_valuetype type; + napi_typeof(env, value, &type); + return type == napi_string; +} + +/** + * Returns true if 'value' is a buffer. + */ +static bool IsBuffer (napi_env env, napi_value value) { + bool isBuffer; + napi_is_buffer(env, value, &isBuffer); + return isBuffer; +} + +/** + * Returns true if 'value' is an object. + */ +static bool IsObject (napi_env env, napi_value value) { + napi_valuetype type; + napi_typeof(env, value, &type); + return type == napi_object; +} + +/** + * Create an error object. + */ +static napi_value CreateError (napi_env env, const char* str) { + napi_value msg; + napi_create_string_utf8(env, str, strlen(str), &msg); + napi_value error; + napi_create_error(env, NULL, msg, &error); + return error; +} + +static napi_value CreateCodeError (napi_env env, const char* code, const char* msg) { + napi_value codeValue; + napi_create_string_utf8(env, code, strlen(code), &codeValue); + napi_value msgValue; + napi_create_string_utf8(env, msg, strlen(msg), &msgValue); + napi_value error; + napi_create_error(env, codeValue, msgValue, &error); + return error; +} + +/** + * Returns true if 'obj' has a property 'key'. + */ +static bool HasProperty (napi_env env, napi_value obj, const char* key) { + bool has = false; + napi_has_named_property(env, obj, key, &has); + return has; +} + +/** + * Returns a property in napi_value form. + */ +static napi_value GetProperty (napi_env env, napi_value obj, const char* key) { + napi_value value; + napi_get_named_property(env, obj, key, &value); + return value; +} + +/** + * Returns a boolean property 'key' from 'obj'. + * Returns 'DEFAULT' if the property doesn't exist. + */ +static bool BooleanProperty (napi_env env, napi_value obj, const char* key, + bool DEFAULT) { + if (HasProperty(env, obj, key)) { + napi_value value = GetProperty(env, obj, key); + bool result; + napi_get_value_bool(env, value, &result); + return result; + } + + return DEFAULT; +} + +enum Encoding { buffer, utf8, view }; + +/** + * Returns internal Encoding enum matching the given encoding option. + */ +static Encoding GetEncoding (napi_env env, napi_value options, const char* option) { + napi_value value; + size_t copied; + char buf[2]; + + if (napi_get_named_property(env, options, option, &value) == napi_ok && + napi_get_value_string_utf8(env, value, buf, 2, &copied) == napi_ok && copied == 1) { + // Value is either "buffer", "utf8" or "view" so we only have to read the first char + switch (buf[0]) { + case 'b': return Encoding::buffer; + case 'v': return Encoding::view; + } + } + + return Encoding::utf8; +} + +/** + * Returns a uint32 property 'key' from 'obj'. + * Returns 'DEFAULT' if the property doesn't exist. + */ +static uint32_t Uint32Property (napi_env env, napi_value obj, const char* key, + uint32_t DEFAULT) { + if (HasProperty(env, obj, key)) { + napi_value value = GetProperty(env, obj, key); + uint32_t result; + napi_get_value_uint32(env, value, &result); + return result; + } + + return DEFAULT; +} + +/** + * Returns a int32 property 'key' from 'obj'. + * Returns 'DEFAULT' if the property doesn't exist. + */ +static int Int32Property (napi_env env, napi_value obj, const char* key, + int DEFAULT) { + if (HasProperty(env, obj, key)) { + napi_value value = GetProperty(env, obj, key); + int result; + napi_get_value_int32(env, value, &result); + return result; + } + + return DEFAULT; +} + +/** + * Returns a string property 'key' from 'obj'. + * Returns empty string if the property doesn't exist. + */ +static std::string StringProperty (napi_env env, napi_value obj, const char* key) { + if (HasProperty(env, obj, key)) { + napi_value value = GetProperty(env, obj, key); + if (IsString(env, value)) { + size_t size = 0; + napi_get_value_string_utf8(env, value, NULL, 0, &size); + + char* buf = new char[size + 1]; + napi_get_value_string_utf8(env, value, buf, size + 1, &size); + buf[size] = '\0'; + + std::string result = buf; + delete [] buf; + return result; + } + } + + return ""; +} + +static void DisposeSliceBuffer (leveldb::Slice slice) { + if (!slice.empty()) delete [] slice.data(); +} + +/** + * Convert a napi_value to a leveldb::Slice. + */ +static leveldb::Slice ToSlice (napi_env env, napi_value from) { + LD_STRING_OR_BUFFER_TO_COPY(env, from, to); + return leveldb::Slice(toCh_, toSz_); +} + +/** + * Takes a Buffer, string or Uint8Array property 'name' from 'opts'. + * Returns null if the property does not exist. + */ +static std::string* RangeOption (napi_env env, napi_value opts, const char* name) { + if (HasProperty(env, opts, name)) { + napi_value value = GetProperty(env, opts, name); + // TODO: we can avoid a copy here + LD_STRING_OR_BUFFER_TO_COPY(env, value, to); + std::string* result = new std::string(toCh_, toSz_); + delete [] toCh_; + return result; + } + + return NULL; +} + +/** + * Converts an array containing Buffer or string keys to a vector. + */ +static std::vector KeyArray (napi_env env, napi_value arr) { + uint32_t length; + std::vector result; + + if (napi_get_array_length(env, arr, &length) == napi_ok) { + result.reserve(length); + + for (uint32_t i = 0; i < length; i++) { + napi_value element; + + if (napi_get_element(env, arr, i, &element) == napi_ok) { + LD_STRING_OR_BUFFER_TO_COPY(env, element, to); + result.emplace_back(toCh_, toSz_); + delete [] toCh_; + } + } + } + + return result; +} + +/** + * Calls a function. + */ +static napi_status CallFunction (napi_env env, + napi_value callback, + const int argc, + napi_value* argv) { + napi_value global; + napi_get_global(env, &global); + return napi_call_function(env, global, callback, argc, argv, NULL); +} + +/** + * Whether to yield entries, keys or values. + */ +enum Mode { + entries, + keys, + values +}; + +/** + * Helper struct for caching and converting a key-value pair to napi_values. + */ +struct Entry { + Entry (const leveldb::Slice& key, const leveldb::Slice& value) + : key_(key.data(), key.size()), + value_(value.data(), value.size()) {} + + void ConvertByMode (napi_env env, Mode mode, const Encoding keyEncoding, const Encoding valueEncoding, napi_value& result) const { + if (mode == Mode::entries) { + napi_create_array_with_length(env, 2, &result); + + napi_value keyElement; + napi_value valueElement; + + Convert(env, &key_, keyEncoding, keyElement); + Convert(env, &value_, valueEncoding, valueElement); + + napi_set_element(env, result, 0, keyElement); + napi_set_element(env, result, 1, valueElement); + } else if (mode == Mode::keys) { + Convert(env, &key_, keyEncoding, result); + } else { + Convert(env, &value_, valueEncoding, result); + } + } + + static void Convert (napi_env env, const std::string* s, const Encoding encoding, napi_value& result) { + if (s == NULL) { + napi_get_undefined(env, &result); + } else if (encoding == Encoding::buffer || encoding == Encoding::view) { + napi_create_buffer_copy(env, s->size(), s->data(), NULL, &result); + } else { + napi_create_string_utf8(env, s->data(), s->size(), &result); + } + } + +private: + std::string key_; + std::string value_; +}; + +/** + * Base worker class. Handles the async work. Derived classes can override the + * following virtual methods (listed in the order in which they're called): + * + * - DoExecute (abstract, worker pool thread): main work + * - HandleOKCallback (main thread): call JS callback on success + * - HandleErrorCallback (main thread): call JS callback on error + * - DoFinally (main thread): do cleanup regardless of success + */ +struct BaseWorker { + // Note: storing env is discouraged as we'd end up using it in unsafe places. + BaseWorker (napi_env env, + Database* database, + napi_value callback, + const char* resourceName) + : database_(database), errMsg_(NULL) { + NAPI_STATUS_THROWS_VOID(napi_create_reference(env, callback, 1, &callbackRef_)); + napi_value asyncResourceName; + NAPI_STATUS_THROWS_VOID(napi_create_string_utf8(env, resourceName, + NAPI_AUTO_LENGTH, + &asyncResourceName)); + NAPI_STATUS_THROWS_VOID(napi_create_async_work(env, callback, + asyncResourceName, + BaseWorker::Execute, + BaseWorker::Complete, + this, &asyncWork_)); + } + + virtual ~BaseWorker () { + delete [] errMsg_; + } + + static void Execute (napi_env env, void* data) { + BaseWorker* self = (BaseWorker*)data; + + // Don't pass env to DoExecute() because use of Node-API + // methods should generally be avoided in async work. + self->DoExecute(); + } + + bool SetStatus (leveldb::Status status) { + status_ = status; + if (!status.ok()) { + SetErrorMessage(status.ToString().c_str()); + return false; + } + return true; + } + + void SetErrorMessage(const char *msg) { + delete [] errMsg_; + size_t size = strlen(msg) + 1; + errMsg_ = new char[size]; + memcpy(errMsg_, msg, size); + } + + virtual void DoExecute () = 0; + + static void Complete (napi_env env, napi_status status, void* data) { + BaseWorker* self = (BaseWorker*)data; + + self->DoComplete(env); + self->DoFinally(env); + } + + void DoComplete (napi_env env) { + napi_value callback; + napi_get_reference_value(env, callbackRef_, &callback); + + if (status_.ok()) { + HandleOKCallback(env, callback); + } else { + HandleErrorCallback(env, callback); + } + } + + virtual void HandleOKCallback (napi_env env, napi_value callback) { + napi_value argv; + napi_get_null(env, &argv); + CallFunction(env, callback, 1, &argv); + } + + virtual void HandleErrorCallback (napi_env env, napi_value callback) { + napi_value argv; + + if (status_.IsNotFound()) { + argv = CreateCodeError(env, "LEVEL_NOT_FOUND", errMsg_); + } else if (status_.IsCorruption()) { + argv = CreateCodeError(env, "LEVEL_CORRUPTION", errMsg_); + } else if (status_.IsIOError()) { + if (strlen(errMsg_) > 15 && strncmp("IO error: lock ", errMsg_, 15) == 0) { // env_posix.cc + argv = CreateCodeError(env, "LEVEL_LOCKED", errMsg_); + } else if (strlen(errMsg_) > 19 && strncmp("IO error: LockFile ", errMsg_, 19) == 0) { // env_win.cc + argv = CreateCodeError(env, "LEVEL_LOCKED", errMsg_); + } else { + argv = CreateCodeError(env, "LEVEL_IO_ERROR", errMsg_); + } + } else { + argv = CreateError(env, errMsg_); + } + + CallFunction(env, callback, 1, &argv); + } + + virtual void DoFinally (napi_env env) { + napi_delete_reference(env, callbackRef_); + napi_delete_async_work(env, asyncWork_); + + delete this; + } + + void Queue (napi_env env) { + napi_queue_async_work(env, asyncWork_); + } + + Database* database_; + +private: + napi_ref callbackRef_; + napi_async_work asyncWork_; + leveldb::Status status_; + char *errMsg_; +}; + +/** + * Owns the LevelDB storage, cache, filter policy and iterators. + */ +struct Database { + Database () + : db_(NULL), + blockCache_(NULL), + filterPolicy_(leveldb::NewBloomFilterPolicy(10)), + currentIteratorId_(0), + pendingCloseWorker_(NULL), + ref_(NULL), + priorityWork_(0) {} + + ~Database () { + if (db_ != NULL) { + threadsafe_close(*this); + } + } + + leveldb::Status Open (const leveldb::Options& options, + const std::string &location, + bool multithreading) { + location_ = location; + return threadsafe_open(options, multithreading, *this); + } + + void CloseDatabase () { + if (db_ != NULL) { + threadsafe_close(*this); + } + if (blockCache_) { + delete blockCache_; + blockCache_ = NULL; + } + } + + leveldb::Status Put (const leveldb::WriteOptions& options, + leveldb::Slice key, + leveldb::Slice value) { + return db_->Put(options, key, value); + } + + leveldb::Status Get (const leveldb::ReadOptions& options, + leveldb::Slice key, + std::string& value) { + return db_->Get(options, key, &value); + } + + leveldb::Status Del (const leveldb::WriteOptions& options, + leveldb::Slice key) { + return db_->Delete(options, key); + } + + leveldb::Status WriteBatch (const leveldb::WriteOptions& options, + leveldb::WriteBatch* batch) { + return db_->Write(options, batch); + } + + uint64_t ApproximateSize (const leveldb::Range* range) { + uint64_t size = 0; + db_->GetApproximateSizes(range, 1, &size); + return size; + } + + void CompactRange (const leveldb::Slice* start, + const leveldb::Slice* end) { + db_->CompactRange(start, end); + } + + void GetProperty (const leveldb::Slice& property, std::string* value) { + db_->GetProperty(property, value); + } + + const leveldb::Snapshot* NewSnapshot () { + return db_->GetSnapshot(); + } + + leveldb::Iterator* NewIterator (leveldb::ReadOptions* options) { + return db_->NewIterator(*options); + } + + void ReleaseSnapshot (const leveldb::Snapshot* snapshot) { + return db_->ReleaseSnapshot(snapshot); + } + + void AttachIterator (napi_env env, uint32_t id, Iterator* iterator) { + iterators_[id] = iterator; + IncrementPriorityWork(env); + } + + void DetachIterator (napi_env env, uint32_t id) { + iterators_.erase(id); + DecrementPriorityWork(env); + } + + void IncrementPriorityWork (napi_env env) { + napi_reference_ref(env, ref_, &priorityWork_); + } + + void DecrementPriorityWork (napi_env env) { + napi_reference_unref(env, ref_, &priorityWork_); + + if (priorityWork_ == 0 && pendingCloseWorker_ != NULL) { + pendingCloseWorker_->Queue(env); + pendingCloseWorker_ = NULL; + } + } + + bool HasPriorityWork () const { + return priorityWork_ > 0; + } + + leveldb::DB* db_; + leveldb::Cache* blockCache_; + const leveldb::FilterPolicy* filterPolicy_; + uint32_t currentIteratorId_; + BaseWorker *pendingCloseWorker_; + std::map< uint32_t, Iterator * > iterators_; + napi_ref ref_; + +private: + uint32_t priorityWork_; + std::string location_; + + // for separation of concerns the threadsafe functionality was kept at the global + // level and made a friend so it is explict where the threadsafe boundary exists + friend leveldb::Status threadsafe_open(const leveldb::Options &options, + bool multithreading, + Database &db_instance); + friend leveldb::Status threadsafe_close(Database &db_instance); +}; + + +leveldb::Status threadsafe_open(const leveldb::Options &options, + bool multithreading, + Database &db_instance) { + // Bypass lock and handles if multithreading is disabled + if (!multithreading) { + return leveldb::DB::Open(options, db_instance.location_, &db_instance.db_); + } + + std::unique_lock lock(handles_mutex); + + auto it = db_handles.find(db_instance.location_); + if (it == db_handles.end()) { + // Database not opened yet for this location, unless it was with + // multithreading disabled, in which case we're expected to fail here. + LevelDbHandle handle = {nullptr, 0}; + leveldb::Status status = leveldb::DB::Open(options, db_instance.location_, &handle.db); + + if (status.ok()) { + handle.open_handle_count++; + db_instance.db_ = handle.db; + db_handles[db_instance.location_] = handle; + } + + return status; + } + + ++(it->second.open_handle_count); + db_instance.db_ = it->second.db; + + return leveldb::Status::OK(); +} + +leveldb::Status threadsafe_close(Database &db_instance) { + std::unique_lock lock(handles_mutex); + + auto it = db_handles.find(db_instance.location_); + if (it == db_handles.end()) { + // Was not opened with multithreading enabled + delete db_instance.db_; + } else if (--(it->second.open_handle_count) == 0) { + delete it->second.db; + db_handles.erase(it); + } + + // ensure db_ pointer is nullified in Database instance + db_instance.db_ = NULL; + return leveldb::Status::OK(); +} + +/** + * Base worker class for doing async work that defers closing the database. + */ +struct PriorityWorker : public BaseWorker { + PriorityWorker (napi_env env, Database* database, napi_value callback, const char* resourceName) + : BaseWorker(env, database, callback, resourceName) { + database_->IncrementPriorityWork(env); + } + + virtual ~PriorityWorker () {} + + void DoFinally (napi_env env) override { + database_->DecrementPriorityWork(env); + BaseWorker::DoFinally(env); + } +}; + +/** + * Owns a leveldb iterator. + */ +struct BaseIterator { + BaseIterator(Database* database, + const bool reverse, + std::string* lt, + std::string* lte, + std::string* gt, + std::string* gte, + const int limit, + const bool fillCache) + : database_(database), + hasClosed_(false), + didSeek_(false), + reverse_(reverse), + lt_(lt), + lte_(lte), + gt_(gt), + gte_(gte), + limit_(limit), + count_(0) { + options_ = new leveldb::ReadOptions(); + options_->fill_cache = fillCache; + options_->snapshot = database->NewSnapshot(); + dbIterator_ = database_->NewIterator(options_); + } + + virtual ~BaseIterator () { + assert(hasClosed_); + + if (lt_ != NULL) delete lt_; + if (gt_ != NULL) delete gt_; + if (lte_ != NULL) delete lte_; + if (gte_ != NULL) delete gte_; + + delete options_; + } + + bool DidSeek () const { + return didSeek_; + } + + /** + * Seek to the first relevant key based on range options. + */ + void SeekToRange () { + didSeek_ = true; + + if (!reverse_ && gte_ != NULL) { + dbIterator_->Seek(*gte_); + } else if (!reverse_ && gt_ != NULL) { + dbIterator_->Seek(*gt_); + + if (dbIterator_->Valid() && dbIterator_->key().compare(*gt_) == 0) { + dbIterator_->Next(); + } + } else if (reverse_ && lte_ != NULL) { + dbIterator_->Seek(*lte_); + + if (!dbIterator_->Valid()) { + dbIterator_->SeekToLast(); + } else if (dbIterator_->key().compare(*lte_) > 0) { + dbIterator_->Prev(); + } + } else if (reverse_ && lt_ != NULL) { + dbIterator_->Seek(*lt_); + + if (!dbIterator_->Valid()) { + dbIterator_->SeekToLast(); + } else if (dbIterator_->key().compare(*lt_) >= 0) { + dbIterator_->Prev(); + } + } else if (reverse_) { + dbIterator_->SeekToLast(); + } else { + dbIterator_->SeekToFirst(); + } + } + + /** + * Seek manually (during iteration). + */ + void Seek (leveldb::Slice& target) { + didSeek_ = true; + + if (OutOfRange(target)) { + return SeekToEnd(); + } + + dbIterator_->Seek(target); + + if (dbIterator_->Valid()) { + int cmp = dbIterator_->key().compare(target); + if (reverse_ ? cmp > 0 : cmp < 0) { + Next(); + } + } else { + SeekToFirst(); + if (dbIterator_->Valid()) { + int cmp = dbIterator_->key().compare(target); + if (reverse_ ? cmp > 0 : cmp < 0) { + SeekToEnd(); + } + } + } + } + + void Close () { + if (!hasClosed_) { + hasClosed_ = true; + delete dbIterator_; + dbIterator_ = NULL; + database_->ReleaseSnapshot(options_->snapshot); + } + } + + bool Valid () const { + return dbIterator_->Valid() && !OutOfRange(dbIterator_->key()); + } + + bool Increment () { + return limit_ < 0 || ++count_ <= limit_; + } + + void Next () { + if (reverse_) dbIterator_->Prev(); + else dbIterator_->Next(); + } + + void SeekToFirst () { + if (reverse_) dbIterator_->SeekToLast(); + else dbIterator_->SeekToFirst(); + } + + void SeekToLast () { + if (reverse_) dbIterator_->SeekToFirst(); + else dbIterator_->SeekToLast(); + } + + void SeekToEnd () { + SeekToLast(); + Next(); + } + + leveldb::Slice CurrentKey () const { + return dbIterator_->key(); + } + + leveldb::Slice CurrentValue () const { + return dbIterator_->value(); + } + + leveldb::Status Status () const { + return dbIterator_->status(); + } + + bool OutOfRange (const leveldb::Slice& target) const { + // The lte and gte options take precedence over lt and gt respectively + if (lte_ != NULL) { + if (target.compare(*lte_) > 0) return true; + } else if (lt_ != NULL) { + if (target.compare(*lt_) >= 0) return true; + } + + if (gte_ != NULL) { + if (target.compare(*gte_) < 0) return true; + } else if (gt_ != NULL) { + if (target.compare(*gt_) <= 0) return true; + } + + return false; + } + + Database* database_; + bool hasClosed_; + +private: + leveldb::Iterator* dbIterator_; + bool didSeek_; + const bool reverse_; + std::string* lt_; + std::string* lte_; + std::string* gt_; + std::string* gte_; + const int limit_; + int count_; + leveldb::ReadOptions* options_; +}; + +/** + * Extends BaseIterator for reading it from JS land. + */ +struct Iterator final : public BaseIterator { + Iterator (Database* database, + const uint32_t id, + const bool reverse, + const bool keys, + const bool values, + const int limit, + std::string* lt, + std::string* lte, + std::string* gt, + std::string* gte, + const bool fillCache, + const Encoding keyEncoding, + const Encoding valueEncoding, + const uint32_t highWaterMarkBytes) + : BaseIterator(database, reverse, lt, lte, gt, gte, limit, fillCache), + id_(id), + keys_(keys), + values_(values), + keyEncoding_(keyEncoding), + valueEncoding_(valueEncoding), + highWaterMarkBytes_(highWaterMarkBytes), + first_(true), + nexting_(false), + isClosing_(false), + closeWorker_(NULL), + ref_(NULL) { + } + + ~Iterator () {} + + void Attach (napi_env env, napi_value context) { + napi_create_reference(env, context, 1, &ref_); + database_->AttachIterator(env, id_, this); + } + + void Detach (napi_env env) { + database_->DetachIterator(env, id_); + if (ref_ != NULL) napi_delete_reference(env, ref_); + } + + bool ReadMany (uint32_t size) { + cache_.clear(); + cache_.reserve(size); + size_t bytesRead = 0; + leveldb::Slice empty; + + while (true) { + if (!first_) Next(); + else first_ = false; + + if (!Valid() || !Increment()) break; + + if (keys_ && values_) { + leveldb::Slice k = CurrentKey(); + leveldb::Slice v = CurrentValue(); + cache_.emplace_back(k, v); + bytesRead += k.size() + v.size(); + } else if (keys_) { + leveldb::Slice k = CurrentKey(); + cache_.emplace_back(k, empty); + bytesRead += k.size(); + } else if (values_) { + leveldb::Slice v = CurrentValue(); + cache_.emplace_back(empty, v); + bytesRead += v.size(); + } + + if (bytesRead > highWaterMarkBytes_ || cache_.size() >= size) { + return true; + } + } + + return false; + } + + const uint32_t id_; + const bool keys_; + const bool values_; + const Encoding keyEncoding_; + const Encoding valueEncoding_; + const uint32_t highWaterMarkBytes_; + bool first_; + bool nexting_; + bool isClosing_; + BaseWorker* closeWorker_; + std::vector cache_; + +private: + napi_ref ref_; +}; + +/** + * Hook for when the environment exits. This hook will be called after + * already-scheduled napi_async_work items have finished, which gives us + * the guarantee that no db operations will be in-flight at this time. + */ +static void env_cleanup_hook (void* arg) { + Database* database = (Database*)arg; + + // Do everything that db_close() does but synchronously. We're expecting that GC + // did not (yet) collect the database because that would be a user mistake (not + // closing their db) made during the lifetime of the environment. That's different + // from an environment being torn down (like the main process or a worker thread) + // where it's our responsibility to clean up. Note also, the following code must + // be a safe noop if called before db_open() or after db_close(). + if (database && database->db_ != NULL) { + std::map iterators = database->iterators_; + std::map::iterator it; + + // TODO: does not do `napi_delete_reference(env, iterator->ref_)`. Problem? + for (it = iterators.begin(); it != iterators.end(); ++it) { + it->second->Close(); + } + + // Having closed the iterators (and released snapshots) we can safely close. + database->CloseDatabase(); + } +} + +/** + * Runs when a Database is garbage collected. + */ +static void FinalizeDatabase (napi_env env, void* data, void* hint) { + if (data) { + Database* database = (Database*)data; + napi_remove_env_cleanup_hook(env, env_cleanup_hook, database); + if (database->ref_ != NULL) napi_delete_reference(env, database->ref_); + delete database; + } +} + +/** + * Returns a context object for a database. + */ +NAPI_METHOD(db_init) { + Database* database = new Database(); + napi_add_env_cleanup_hook(env, env_cleanup_hook, database); + + napi_value result; + NAPI_STATUS_THROWS(napi_create_external(env, database, + FinalizeDatabase, + NULL, &result)); + + // Reference counter to prevent GC of database while priority workers are active + NAPI_STATUS_THROWS(napi_create_reference(env, result, 0, &database->ref_)); + + return result; +} + +/** + * Worker class for opening a database. + * TODO: shouldn't this be a PriorityWorker? + */ +struct OpenWorker final : public BaseWorker { + OpenWorker (napi_env env, + Database* database, + napi_value callback, + const std::string& location, + const bool createIfMissing, + const bool errorIfExists, + const bool compression, + const bool multithreading, + const uint32_t writeBufferSize, + const uint32_t blockSize, + const uint32_t maxOpenFiles, + const uint32_t blockRestartInterval, + const uint32_t maxFileSize) + : BaseWorker(env, database, callback, "classic_level.db.open"), + location_(location), + multithreading_(multithreading) { + options_.block_cache = database->blockCache_; + options_.filter_policy = database->filterPolicy_; + options_.create_if_missing = createIfMissing; + options_.error_if_exists = errorIfExists; + options_.compression = compression + ? leveldb::kSnappyCompression + : leveldb::kNoCompression; + options_.write_buffer_size = writeBufferSize; + options_.block_size = blockSize; + options_.max_open_files = maxOpenFiles; + options_.block_restart_interval = blockRestartInterval; + options_.max_file_size = maxFileSize; + } + + ~OpenWorker () {} + + void DoExecute () override { + SetStatus(database_->Open(options_, location_, multithreading_)); + } + + leveldb::Options options_; + std::string location_; + bool multithreading_; +}; + +/** + * Open a database. + */ +NAPI_METHOD(db_open) { + NAPI_ARGV(4); + NAPI_DB_CONTEXT(); + NAPI_ARGV_UTF8_NEW(location, 1); + + napi_value options = argv[2]; + const bool createIfMissing = BooleanProperty(env, options, "createIfMissing", true); + const bool errorIfExists = BooleanProperty(env, options, "errorIfExists", false); + const bool compression = BooleanProperty(env, options, "compression", true); + const bool multithreading = BooleanProperty(env, options, "multithreading", false); + + const uint32_t cacheSize = Uint32Property(env, options, "cacheSize", 8 << 20); + const uint32_t writeBufferSize = Uint32Property(env, options , "writeBufferSize" , 4 << 20); + const uint32_t blockSize = Uint32Property(env, options, "blockSize", 4096); + const uint32_t maxOpenFiles = Uint32Property(env, options, "maxOpenFiles", 1000); + const uint32_t blockRestartInterval = Uint32Property(env, options, + "blockRestartInterval", 16); + const uint32_t maxFileSize = Uint32Property(env, options, "maxFileSize", 2 << 20); + + database->blockCache_ = leveldb::NewLRUCache(cacheSize); + + napi_value callback = argv[3]; + OpenWorker* worker = new OpenWorker(env, database, callback, location, + createIfMissing, errorIfExists, + compression, multithreading, + writeBufferSize, blockSize, + maxOpenFiles, blockRestartInterval, + maxFileSize); + worker->Queue(env); + delete [] location; + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Worker class for closing a database + */ +struct CloseWorker final : public BaseWorker { + CloseWorker (napi_env env, + Database* database, + napi_value callback) + : BaseWorker(env, database, callback, "classic_level.db.close") {} + + ~CloseWorker () {} + + void DoExecute () override { + database_->CloseDatabase(); + } +}; + +napi_value noop_callback (napi_env env, napi_callback_info info) { + return 0; +} + +/** + * Close a database. + */ +NAPI_METHOD(db_close) { + NAPI_ARGV(2); + NAPI_DB_CONTEXT(); + + napi_value callback = argv[1]; + CloseWorker* worker = new CloseWorker(env, database, callback); + + if (!database->HasPriorityWork()) { + worker->Queue(env); + NAPI_RETURN_UNDEFINED(); + } + + database->pendingCloseWorker_ = worker; + + napi_value noop; + napi_create_function(env, NULL, 0, noop_callback, NULL, &noop); + + std::map iterators = database->iterators_; + std::map::iterator it; + + for (it = iterators.begin(); it != iterators.end(); ++it) { + iterator_close_do(env, it->second, noop); + } + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Worker class for putting key/value to the database + */ +struct PutWorker final : public PriorityWorker { + PutWorker (napi_env env, + Database* database, + napi_value callback, + leveldb::Slice key, + leveldb::Slice value, + bool sync) + : PriorityWorker(env, database, callback, "classic_level.db.put"), + key_(key), value_(value) { + options_.sync = sync; + } + + ~PutWorker () { + DisposeSliceBuffer(key_); + DisposeSliceBuffer(value_); + } + + void DoExecute () override { + SetStatus(database_->Put(options_, key_, value_)); + } + + leveldb::WriteOptions options_; + leveldb::Slice key_; + leveldb::Slice value_; +}; + +/** + * Puts a key and a value to a database. + */ +NAPI_METHOD(db_put) { + NAPI_ARGV(5); + NAPI_DB_CONTEXT(); + + leveldb::Slice key = ToSlice(env, argv[1]); + leveldb::Slice value = ToSlice(env, argv[2]); + bool sync = BooleanProperty(env, argv[3], "sync", false); + napi_value callback = argv[4]; + + PutWorker* worker = new PutWorker(env, database, callback, key, value, sync); + worker->Queue(env); + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Worker class for getting a value from a database. + */ +struct GetWorker final : public PriorityWorker { + GetWorker (napi_env env, + Database* database, + napi_value callback, + leveldb::Slice key, + const Encoding encoding, + const bool fillCache) + : PriorityWorker(env, database, callback, "classic_level.db.get"), + key_(key), + encoding_(encoding) { + options_.fill_cache = fillCache; + } + + ~GetWorker () { + DisposeSliceBuffer(key_); + } + + void DoExecute () override { + SetStatus(database_->Get(options_, key_, value_)); + } + + void HandleOKCallback (napi_env env, napi_value callback) override { + napi_value argv[2]; + napi_get_null(env, &argv[0]); + Entry::Convert(env, &value_, encoding_, argv[1]); + CallFunction(env, callback, 2, argv); + } + +private: + leveldb::ReadOptions options_; + leveldb::Slice key_; + std::string value_; + const Encoding encoding_; +}; + +/** + * Gets a value from a database. + */ +NAPI_METHOD(db_get) { + NAPI_ARGV(4); + NAPI_DB_CONTEXT(); + + leveldb::Slice key = ToSlice(env, argv[1]); + napi_value options = argv[2]; + const Encoding encoding = GetEncoding(env, options, "valueEncoding"); + const bool fillCache = BooleanProperty(env, options, "fillCache", true); + napi_value callback = argv[3]; + + GetWorker* worker = new GetWorker(env, database, callback, key, encoding, + fillCache); + worker->Queue(env); + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Worker class for getting many values. + */ +struct GetManyWorker final : public PriorityWorker { + GetManyWorker (napi_env env, + Database* database, + std::vector keys, + napi_value callback, + const Encoding valueEncoding, + const bool fillCache) + : PriorityWorker(env, database, callback, "classic_level.get.many"), + keys_(std::move(keys)), valueEncoding_(valueEncoding) { + options_.fill_cache = fillCache; + options_.snapshot = database->NewSnapshot(); + } + + void DoExecute () override { + cache_.reserve(keys_.size()); + + for (const std::string& key: keys_) { + std::string* value = new std::string(); + leveldb::Status status = database_->Get(options_, key, *value); + + if (status.ok()) { + cache_.push_back(value); + } else if (status.IsNotFound()) { + delete value; + cache_.push_back(NULL); + } else { + delete value; + for (const std::string* value: cache_) { + if (value != NULL) delete value; + } + SetStatus(status); + break; + } + } + + database_->ReleaseSnapshot(options_.snapshot); + } + + void HandleOKCallback (napi_env env, napi_value callback) override { + size_t size = cache_.size(); + napi_value array; + napi_create_array_with_length(env, size, &array); + + for (size_t idx = 0; idx < size; idx++) { + std::string* value = cache_[idx]; + napi_value element; + Entry::Convert(env, value, valueEncoding_, element); + napi_set_element(env, array, static_cast(idx), element); + if (value != NULL) delete value; + } + + napi_value argv[2]; + napi_get_null(env, &argv[0]); + argv[1] = array; + CallFunction(env, callback, 2, argv); + } + +private: + leveldb::ReadOptions options_; + const std::vector keys_; + const Encoding valueEncoding_; + std::vector cache_; +}; + +/** + * Gets many values from a database. + */ +NAPI_METHOD(db_get_many) { + NAPI_ARGV(4); + NAPI_DB_CONTEXT(); + + const auto keys = KeyArray(env, argv[1]); + napi_value options = argv[2]; + const Encoding valueEncoding = GetEncoding(env, options, "valueEncoding"); + const bool fillCache = BooleanProperty(env, options, "fillCache", true); + napi_value callback = argv[3]; + + GetManyWorker* worker = new GetManyWorker( + env, database, keys, callback, valueEncoding, fillCache + ); + + worker->Queue(env); + NAPI_RETURN_UNDEFINED(); +} + +/** + * Worker class for deleting a value from a database. + */ +struct DelWorker final : public PriorityWorker { + DelWorker (napi_env env, + Database* database, + napi_value callback, + leveldb::Slice key, + bool sync) + : PriorityWorker(env, database, callback, "classic_level.db.del"), + key_(key) { + options_.sync = sync; + } + + ~DelWorker () { + DisposeSliceBuffer(key_); + } + + void DoExecute () override { + SetStatus(database_->Del(options_, key_)); + } + + leveldb::WriteOptions options_; + leveldb::Slice key_; +}; + +/** + * Delete a value from a database. + */ +NAPI_METHOD(db_del) { + NAPI_ARGV(4); + NAPI_DB_CONTEXT(); + + leveldb::Slice key = ToSlice(env, argv[1]); + bool sync = BooleanProperty(env, argv[2], "sync", false); + napi_value callback = argv[3]; + + DelWorker* worker = new DelWorker(env, database, callback, key, sync); + worker->Queue(env); + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Worker class for deleting a range from a database. + */ +struct ClearWorker final : public PriorityWorker { + ClearWorker (napi_env env, + Database* database, + napi_value callback, + const bool reverse, + const int limit, + std::string* lt, + std::string* lte, + std::string* gt, + std::string* gte) + : PriorityWorker(env, database, callback, "classic_level.db.clear") { + iterator_ = new BaseIterator(database, reverse, lt, lte, gt, gte, limit, false); + writeOptions_ = new leveldb::WriteOptions(); + writeOptions_->sync = false; + } + + ~ClearWorker () { + delete iterator_; + delete writeOptions_; + } + + void DoExecute () override { + iterator_->SeekToRange(); + + // TODO: add option + uint32_t hwm = 16 * 1024; + leveldb::WriteBatch batch; + + while (true) { + size_t bytesRead = 0; + + while (bytesRead <= hwm && iterator_->Valid() && iterator_->Increment()) { + leveldb::Slice key = iterator_->CurrentKey(); + batch.Delete(key); + bytesRead += key.size(); + iterator_->Next(); + } + + if (!SetStatus(iterator_->Status()) || bytesRead == 0) { + break; + } + + if (!SetStatus(database_->WriteBatch(*writeOptions_, &batch))) { + break; + } + + batch.Clear(); + } + + iterator_->Close(); + } + +private: + BaseIterator* iterator_; + leveldb::WriteOptions* writeOptions_; +}; + +/** + * Delete a range from a database. + */ +NAPI_METHOD(db_clear) { + NAPI_ARGV(3); + NAPI_DB_CONTEXT(); + + napi_value options = argv[1]; + napi_value callback = argv[2]; + + const bool reverse = BooleanProperty(env, options, "reverse", false); + const int limit = Int32Property(env, options, "limit", -1); + + std::string* lt = RangeOption(env, options, "lt"); + std::string* lte = RangeOption(env, options, "lte"); + std::string* gt = RangeOption(env, options, "gt"); + std::string* gte = RangeOption(env, options, "gte"); + + ClearWorker* worker = new ClearWorker(env, database, callback, reverse, limit, lt, lte, gt, gte); + worker->Queue(env); + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Worker class for calculating the size of a range. + */ +struct ApproximateSizeWorker final : public PriorityWorker { + ApproximateSizeWorker (napi_env env, + Database* database, + napi_value callback, + leveldb::Slice start, + leveldb::Slice end) + : PriorityWorker(env, database, callback, "classic_level.db.approximate_size"), + start_(start), end_(end) {} + + ~ApproximateSizeWorker () { + DisposeSliceBuffer(start_); + DisposeSliceBuffer(end_); + } + + void DoExecute () override { + leveldb::Range range(start_, end_); + size_ = database_->ApproximateSize(&range); + } + + void HandleOKCallback (napi_env env, napi_value callback) override { + napi_value argv[2]; + napi_get_null(env, &argv[0]); + napi_create_int64(env, (uint64_t)size_, &argv[1]); + CallFunction(env, callback, 2, argv); + } + + leveldb::Slice start_; + leveldb::Slice end_; + uint64_t size_; +}; + +/** + * Calculates the approximate size of a range in a database. + */ +NAPI_METHOD(db_approximate_size) { + NAPI_ARGV(4); + NAPI_DB_CONTEXT(); + + leveldb::Slice start = ToSlice(env, argv[1]); + leveldb::Slice end = ToSlice(env, argv[2]); + + napi_value callback = argv[3]; + + ApproximateSizeWorker* worker = new ApproximateSizeWorker(env, database, + callback, start, + end); + worker->Queue(env); + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Worker class for compacting a range in a database. + */ +struct CompactRangeWorker final : public PriorityWorker { + CompactRangeWorker (napi_env env, + Database* database, + napi_value callback, + leveldb::Slice start, + leveldb::Slice end) + : PriorityWorker(env, database, callback, "classic_level.db.compact_range"), + start_(start), end_(end) {} + + ~CompactRangeWorker () { + DisposeSliceBuffer(start_); + DisposeSliceBuffer(end_); + } + + void DoExecute () override { + database_->CompactRange(&start_, &end_); + } + + leveldb::Slice start_; + leveldb::Slice end_; +}; + +/** + * Compacts a range in a database. + */ +NAPI_METHOD(db_compact_range) { + NAPI_ARGV(4); + NAPI_DB_CONTEXT(); + + leveldb::Slice start = ToSlice(env, argv[1]); + leveldb::Slice end = ToSlice(env, argv[2]); + napi_value callback = argv[3]; + + CompactRangeWorker* worker = new CompactRangeWorker(env, database, callback, + start, end); + worker->Queue(env); + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Get a property from a database. + */ +NAPI_METHOD(db_get_property) { + NAPI_ARGV(2); + NAPI_DB_CONTEXT(); + + leveldb::Slice property = ToSlice(env, argv[1]); + + std::string value; + database->GetProperty(property, &value); + + napi_value result; + napi_create_string_utf8(env, value.data(), value.size(), &result); + + DisposeSliceBuffer(property); + + return result; +} + +/** + * Worker class for destroying a database. + */ +struct DestroyWorker final : public BaseWorker { + DestroyWorker (napi_env env, + const std::string& location, + napi_value callback) + : BaseWorker(env, NULL, callback, "classic_level.destroy_db"), + location_(location) {} + + ~DestroyWorker () {} + + void DoExecute () override { + leveldb::Options options; + SetStatus(leveldb::DestroyDB(location_, options)); + } + + std::string location_; +}; + +/** + * Destroys a database. + */ +NAPI_METHOD(destroy_db) { + NAPI_ARGV(2); + NAPI_ARGV_UTF8_NEW(location, 0); + napi_value callback = argv[1]; + + DestroyWorker* worker = new DestroyWorker(env, location, callback); + worker->Queue(env); + + delete [] location; + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Worker class for repairing a database. + */ +struct RepairWorker final : public BaseWorker { + RepairWorker (napi_env env, + const std::string& location, + napi_value callback) + : BaseWorker(env, NULL, callback, "classic_level.repair_db"), + location_(location) {} + + ~RepairWorker () {} + + void DoExecute () override { + leveldb::Options options; + SetStatus(leveldb::RepairDB(location_, options)); + } + + std::string location_; +}; + +/** + * Repairs a database. + */ +NAPI_METHOD(repair_db) { + NAPI_ARGV(2); + NAPI_ARGV_UTF8_NEW(location, 0); + napi_value callback = argv[1]; + + RepairWorker* worker = new RepairWorker(env, location, callback); + worker->Queue(env); + + delete [] location; + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Runs when an Iterator is garbage collected. + */ +static void FinalizeIterator (napi_env env, void* data, void* hint) { + if (data) { + delete (Iterator*)data; + } +} + +/** + * Create an iterator. + */ +NAPI_METHOD(iterator_init) { + NAPI_ARGV(2); + NAPI_DB_CONTEXT(); + + napi_value options = argv[1]; + const bool reverse = BooleanProperty(env, options, "reverse", false); + const bool keys = BooleanProperty(env, options, "keys", true); + const bool values = BooleanProperty(env, options, "values", true); + const bool fillCache = BooleanProperty(env, options, "fillCache", false); + const Encoding keyEncoding = GetEncoding(env, options, "keyEncoding"); + const Encoding valueEncoding = GetEncoding(env, options, "valueEncoding"); + const int limit = Int32Property(env, options, "limit", -1); + const uint32_t highWaterMarkBytes = Uint32Property(env, options, "highWaterMarkBytes", 16 * 1024); + + std::string* lt = RangeOption(env, options, "lt"); + std::string* lte = RangeOption(env, options, "lte"); + std::string* gt = RangeOption(env, options, "gt"); + std::string* gte = RangeOption(env, options, "gte"); + + const uint32_t id = database->currentIteratorId_++; + Iterator* iterator = new Iterator(database, id, reverse, keys, + values, limit, lt, lte, gt, gte, fillCache, + keyEncoding, valueEncoding, highWaterMarkBytes); + napi_value result; + + NAPI_STATUS_THROWS(napi_create_external(env, iterator, + FinalizeIterator, + NULL, &result)); + + // Prevent GC of JS object before the iterator is closed (explicitly or on + // db close) and keep track of non-closed iterators to close them on db close. + iterator->Attach(env, result); + + return result; +} + +/** + * Seeks an iterator. + */ +NAPI_METHOD(iterator_seek) { + NAPI_ARGV(2); + NAPI_ITERATOR_CONTEXT(); + + if (iterator->isClosing_ || iterator->hasClosed_) { + NAPI_RETURN_UNDEFINED(); + } + + leveldb::Slice target = ToSlice(env, argv[1]); + iterator->first_ = true; + iterator->Seek(target); + + DisposeSliceBuffer(target); + NAPI_RETURN_UNDEFINED(); +} + +/** + * Worker class for closing an iterator + */ +struct CloseIteratorWorker final : public BaseWorker { + CloseIteratorWorker (napi_env env, + Iterator* iterator, + napi_value callback) + : BaseWorker(env, iterator->database_, callback, "classic_level.iterator.close"), + iterator_(iterator) {} + + ~CloseIteratorWorker () {} + + void DoExecute () override { + iterator_->Close(); + } + + void DoFinally (napi_env env) override { + iterator_->Detach(env); + BaseWorker::DoFinally(env); + } + +private: + Iterator* iterator_; +}; + +/** + * Called by NAPI_METHOD(iterator_close) and also when closing + * open iterators during NAPI_METHOD(db_close). + */ +static void iterator_close_do (napi_env env, Iterator* iterator, napi_value cb) { + if (!iterator->isClosing_ && !iterator->hasClosed_) { + CloseIteratorWorker* worker = new CloseIteratorWorker(env, iterator, cb); + iterator->isClosing_ = true; + + if (iterator->nexting_) { + iterator->closeWorker_ = worker; + } else { + worker->Queue(env); + } + } +} + +/** + * Closes an iterator. + */ +NAPI_METHOD(iterator_close) { + NAPI_ARGV(2); + NAPI_ITERATOR_CONTEXT(); + + iterator_close_do(env, iterator, argv[1]); + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Worker class for nexting an iterator. + */ +struct NextWorker final : public BaseWorker { + NextWorker (napi_env env, + Iterator* iterator, + uint32_t size, + napi_value callback) + : BaseWorker(env, iterator->database_, callback, + "classic_level.iterator.next"), + iterator_(iterator), size_(size), ok_() {} + + ~NextWorker () {} + + void DoExecute () override { + if (!iterator_->DidSeek()) { + iterator_->SeekToRange(); + } + + ok_ = iterator_->ReadMany(size_); + + if (!ok_) { + SetStatus(iterator_->Status()); + } + } + + void HandleOKCallback (napi_env env, napi_value callback) override { + size_t size = iterator_->cache_.size(); + napi_value jsArray; + napi_create_array_with_length(env, size, &jsArray); + + const Encoding ke = iterator_->keyEncoding_; + const Encoding ve = iterator_->valueEncoding_; + + for (uint32_t idx = 0; idx < size; idx++) { + napi_value element; + iterator_->cache_[idx].ConvertByMode(env, Mode::entries, ke, ve, element); + napi_set_element(env, jsArray, idx, element); + } + + napi_value argv[3]; + napi_get_null(env, &argv[0]); + argv[1] = jsArray; + napi_get_boolean(env, !ok_, &argv[2]); + CallFunction(env, callback, 3, argv); + } + + void DoFinally (napi_env env) override { + // clean up & handle the next/close state + iterator_->nexting_ = false; + + if (iterator_->closeWorker_ != NULL) { + iterator_->closeWorker_->Queue(env); + iterator_->closeWorker_ = NULL; + } + + BaseWorker::DoFinally(env); + } + +private: + Iterator* iterator_; + uint32_t size_; + bool ok_; +}; + +/** + * Advance repeatedly and get multiple entries at once. + */ +NAPI_METHOD(iterator_nextv) { + NAPI_ARGV(3); + NAPI_ITERATOR_CONTEXT(); + + uint32_t size; + NAPI_STATUS_THROWS(napi_get_value_uint32(env, argv[1], &size)); + if (size == 0) size = 1; + + napi_value callback = argv[2]; + + if (iterator->isClosing_ || iterator->hasClosed_) { + napi_value argv = CreateCodeError(env, "LEVEL_ITERATOR_NOT_OPEN", "Iterator is not open"); + NAPI_STATUS_THROWS(CallFunction(env, callback, 1, &argv)); + NAPI_RETURN_UNDEFINED(); + } + + NextWorker* worker = new NextWorker(env, iterator, size, callback); + iterator->nexting_ = true; + worker->Queue(env); + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Worker class for batch write operation. + */ +struct BatchWorker final : public PriorityWorker { + BatchWorker (napi_env env, + Database* database, + napi_value callback, + leveldb::WriteBatch* batch, + const bool sync, + const bool hasData) + : PriorityWorker(env, database, callback, "classic_level.batch.do"), + batch_(batch), hasData_(hasData) { + options_.sync = sync; + } + + ~BatchWorker () { + delete batch_; + } + + void DoExecute () override { + if (hasData_) { + SetStatus(database_->WriteBatch(options_, batch_)); + } + } + +private: + leveldb::WriteOptions options_; + leveldb::WriteBatch* batch_; + const bool hasData_; +}; + +/** + * Does a batch write operation on a database. + */ +NAPI_METHOD(batch_do) { + NAPI_ARGV(4); + NAPI_DB_CONTEXT(); + + napi_value array = argv[1]; + const bool sync = BooleanProperty(env, argv[2], "sync", false); + napi_value callback = argv[3]; + + uint32_t length; + napi_get_array_length(env, array, &length); + + leveldb::WriteBatch* batch = new leveldb::WriteBatch(); + bool hasData = false; + + for (uint32_t i = 0; i < length; i++) { + napi_value element; + napi_get_element(env, array, i, &element); + + if (!IsObject(env, element)) continue; + + std::string type = StringProperty(env, element, "type"); + + if (type == "del") { + if (!HasProperty(env, element, "key")) continue; + leveldb::Slice key = ToSlice(env, GetProperty(env, element, "key")); + + batch->Delete(key); + if (!hasData) hasData = true; + + DisposeSliceBuffer(key); + } else if (type == "put") { + if (!HasProperty(env, element, "key")) continue; + if (!HasProperty(env, element, "value")) continue; + + leveldb::Slice key = ToSlice(env, GetProperty(env, element, "key")); + leveldb::Slice value = ToSlice(env, GetProperty(env, element, "value")); + + batch->Put(key, value); + if (!hasData) hasData = true; + + DisposeSliceBuffer(key); + DisposeSliceBuffer(value); + } + } + + BatchWorker* worker = new BatchWorker(env, database, callback, batch, sync, hasData); + worker->Queue(env); + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Owns a WriteBatch. + */ +struct Batch { + Batch (Database* database) + : database_(database), + batch_(new leveldb::WriteBatch()), + hasData_(false) {} + + ~Batch () { + delete batch_; + } + + void Put (leveldb::Slice key, leveldb::Slice value) { + batch_->Put(key, value); + hasData_ = true; + } + + void Del (leveldb::Slice key) { + batch_->Delete(key); + hasData_ = true; + } + + void Clear () { + batch_->Clear(); + hasData_ = false; + } + + leveldb::Status Write (bool sync) { + leveldb::WriteOptions options; + options.sync = sync; + return database_->WriteBatch(options, batch_); + } + + Database* database_; + leveldb::WriteBatch* batch_; + bool hasData_; +}; + +/** + * Runs when a Batch is garbage collected. + */ +static void FinalizeBatch (napi_env env, void* data, void* hint) { + if (data) { + delete (Batch*)data; + } +} + +/** + * Return a batch object. + */ +NAPI_METHOD(batch_init) { + NAPI_ARGV(1); + NAPI_DB_CONTEXT(); + + Batch* batch = new Batch(database); + + napi_value result; + NAPI_STATUS_THROWS(napi_create_external(env, batch, + FinalizeBatch, + NULL, &result)); + return result; +} + +/** + * Adds a put instruction to a batch object. + */ +NAPI_METHOD(batch_put) { + NAPI_ARGV(3); + NAPI_BATCH_CONTEXT(); + + leveldb::Slice key = ToSlice(env, argv[1]); + leveldb::Slice value = ToSlice(env, argv[2]); + batch->Put(key, value); + DisposeSliceBuffer(key); + DisposeSliceBuffer(value); + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Adds a delete instruction to a batch object. + */ +NAPI_METHOD(batch_del) { + NAPI_ARGV(2); + NAPI_BATCH_CONTEXT(); + + leveldb::Slice key = ToSlice(env, argv[1]); + batch->Del(key); + DisposeSliceBuffer(key); + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Clears a batch object. + */ +NAPI_METHOD(batch_clear) { + NAPI_ARGV(1); + NAPI_BATCH_CONTEXT(); + + batch->Clear(); + + NAPI_RETURN_UNDEFINED(); +} + +/** + * Worker class for batch write operation. + */ +struct BatchWriteWorker final : public PriorityWorker { + BatchWriteWorker (napi_env env, + napi_value context, + Batch* batch, + napi_value callback, + const bool sync) + : PriorityWorker(env, batch->database_, callback, "classic_level.batch.write"), + batch_(batch), + sync_(sync) { + // Prevent GC of batch object before we execute + NAPI_STATUS_THROWS_VOID(napi_create_reference(env, context, 1, &contextRef_)); + } + + ~BatchWriteWorker () {} + + void DoExecute () override { + if (batch_->hasData_) { + SetStatus(batch_->Write(sync_)); + } + } + + void DoFinally (napi_env env) override { + napi_delete_reference(env, contextRef_); + PriorityWorker::DoFinally(env); + } + +private: + Batch* batch_; + const bool sync_; + napi_ref contextRef_; +}; + +/** + * Writes a batch object. + */ +NAPI_METHOD(batch_write) { + NAPI_ARGV(3); + NAPI_BATCH_CONTEXT(); + + napi_value options = argv[1]; + const bool sync = BooleanProperty(env, options, "sync", false); + napi_value callback = argv[2]; + + BatchWriteWorker* worker = new BatchWriteWorker(env, argv[0], batch, callback, sync); + worker->Queue(env); + + NAPI_RETURN_UNDEFINED(); +} + +/** + * All exported functions. + */ +NAPI_INIT() { + NAPI_EXPORT_FUNCTION(db_init); + NAPI_EXPORT_FUNCTION(db_open); + NAPI_EXPORT_FUNCTION(db_close); + NAPI_EXPORT_FUNCTION(db_put); + NAPI_EXPORT_FUNCTION(db_get); + NAPI_EXPORT_FUNCTION(db_get_many); + NAPI_EXPORT_FUNCTION(db_del); + NAPI_EXPORT_FUNCTION(db_clear); + NAPI_EXPORT_FUNCTION(db_approximate_size); + NAPI_EXPORT_FUNCTION(db_compact_range); + NAPI_EXPORT_FUNCTION(db_get_property); + + NAPI_EXPORT_FUNCTION(destroy_db); + NAPI_EXPORT_FUNCTION(repair_db); + + NAPI_EXPORT_FUNCTION(iterator_init); + NAPI_EXPORT_FUNCTION(iterator_seek); + NAPI_EXPORT_FUNCTION(iterator_close); + NAPI_EXPORT_FUNCTION(iterator_nextv); + + NAPI_EXPORT_FUNCTION(batch_do); + NAPI_EXPORT_FUNCTION(batch_init); + NAPI_EXPORT_FUNCTION(batch_put); + NAPI_EXPORT_FUNCTION(batch_del); + NAPI_EXPORT_FUNCTION(batch_clear); + NAPI_EXPORT_FUNCTION(batch_write); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/binding.gyp b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/binding.gyp new file mode 100644 index 0000000..68b4d1e --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/binding.gyp @@ -0,0 +1,70 @@ +{ + "variables": { + 'openssl_fips': '' + }, + "targets": [{ + "target_name": "classic_level", + "conditions": [ + ["OS == 'win'", { + "defines": [ + "_HAS_EXCEPTIONS=0" + ], + "msvs_settings": { + "VCCLCompilerTool": { + "RuntimeTypeInfo": "false", + "EnableFunctionLevelLinking": "true", + "ExceptionHandling": "2", + "DisableSpecificWarnings": [ "4355", "4530" ,"4267", "4244", "4506" ] + } + } + }], + ["OS == 'linux'", { + "cflags": [], + "cflags!": [ "-fno-tree-vrp"] + }], + ["OS == 'mac'", { + "cflags+": ["-fvisibility=hidden"], + "xcode_settings": { + # -fvisibility=hidden + "GCC_SYMBOLS_PRIVATE_EXTERN": "YES", + + # Set minimum target version because we're building on newer + # Same as https://github.com/nodejs/node/blob/v10.0.0/common.gypi#L416 + "MACOSX_DEPLOYMENT_TARGET": "10.7", + + # Build universal binary to support M1 (Apple silicon) + "OTHER_CFLAGS": [ + "-arch x86_64", + "-arch arm64" + ], + "OTHER_LDFLAGS": [ + "-arch x86_64", + "-arch arm64" + ] + } + }], + ["OS == 'android'", { + "cflags": [ "-fPIC" ], + "ldflags": [ "-fPIC" ], + "cflags!": [ + "-fno-tree-vrp", + "-mfloat-abi=hard", + "-fPIE" + ], + "ldflags!": [ "-fPIE" ] + }], + ["target_arch == 'arm'", { + "cflags": [ "-mfloat-abi=hard" ] + }] + ], + "dependencies": [ + "<(module_root_dir)/deps/leveldb/leveldb.gyp:leveldb" + ], + "include_dirs" : [ + " is present +# -DLEVELDB_PLATFORM_POSIX for Posix-based platforms +# -DSNAPPY if the Snappy library is present +# + +OUTPUT=$1 +PREFIX=$2 +if test -z "$OUTPUT" || test -z "$PREFIX"; then + echo "usage: $0 " >&2 + exit 1 +fi + +# Delete existing output, if it exists +rm -f $OUTPUT +touch $OUTPUT + +if test -z "$CC"; then + CC=cc +fi + +if test -z "$CXX"; then + CXX=g++ +fi + +if test -z "$TMPDIR"; then + TMPDIR=/tmp +fi + +# Detect OS +if test -z "$TARGET_OS"; then + TARGET_OS=`uname -s` +fi + +COMMON_FLAGS= +CROSS_COMPILE= +PLATFORM_CCFLAGS= +PLATFORM_CXXFLAGS= +PLATFORM_LDFLAGS= +PLATFORM_LIBS= +PLATFORM_SHARED_EXT="so" +PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl," +PLATFORM_SHARED_CFLAGS="-fPIC" +PLATFORM_SHARED_VERSIONED=true +PLATFORM_SSEFLAGS= + +MEMCMP_FLAG= +if [ "$CXX" = "g++" ]; then + # Use libc's memcmp instead of GCC's memcmp. This results in ~40% + # performance improvement on readrandom under gcc 4.4.3 on Linux/x86. + MEMCMP_FLAG="-fno-builtin-memcmp" +fi + +case "$TARGET_OS" in + CYGWIN_*) + PLATFORM=OS_LINUX + COMMON_FLAGS="$MEMCMP_FLAG -lpthread -DOS_LINUX -DCYGWIN" + PLATFORM_LDFLAGS="-lpthread" + PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc + ;; + Darwin) + PLATFORM=OS_MACOSX + COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX" + PLATFORM_SHARED_EXT=dylib + [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` + PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name $INSTALL_PATH/" + PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc + ;; + Linux) + PLATFORM=OS_LINUX + COMMON_FLAGS="$MEMCMP_FLAG -pthread -DOS_LINUX" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc + ;; + SunOS) + PLATFORM=OS_SOLARIS + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_SOLARIS" + PLATFORM_LIBS="-lpthread -lrt" + PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc + ;; + FreeBSD) + PLATFORM=OS_FREEBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_FREEBSD" + PLATFORM_LIBS="-lpthread" + PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc + ;; + NetBSD) + PLATFORM=OS_NETBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD" + PLATFORM_LIBS="-lpthread -lgcc_s" + PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc + ;; + OpenBSD) + PLATFORM=OS_OPENBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_OPENBSD" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc + ;; + DragonFly) + PLATFORM=OS_DRAGONFLYBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_DRAGONFLYBSD" + PLATFORM_LIBS="-lpthread" + PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc + ;; + OS_ANDROID_CROSSCOMPILE) + PLATFORM=OS_ANDROID + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX" + PLATFORM_LDFLAGS="" # All pthread features are in the Android C library + PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc + CROSS_COMPILE=true + ;; + HP-UX) + PLATFORM=OS_HPUX + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_HPUX" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc + # man ld: +h internal_name + PLATFORM_SHARED_LDFLAGS="-shared -Wl,+h -Wl," + ;; + IOS) + PLATFORM=IOS + COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX" + [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` + PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc + PLATFORM_SHARED_EXT= + PLATFORM_SHARED_LDFLAGS= + PLATFORM_SHARED_CFLAGS= + PLATFORM_SHARED_VERSIONED= + ;; + *) + echo "Unknown platform!" >&2 + exit 1 +esac + +# We want to make a list of all cc files within util, db, table, and helpers +# except for the test and benchmark files. By default, find will output a list +# of all files matching either rule, so we need to append -print to make the +# prune take effect. +DIRS="$PREFIX/db $PREFIX/util $PREFIX/table" + +set -f # temporarily disable globbing so that our patterns aren't expanded +PRUNE_TEST="-name *test*.cc -prune" +PRUNE_BENCH="-name *_bench.cc -prune" +PRUNE_TOOL="-name leveldbutil.cc -prune" +PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o $PRUNE_TOOL -o -name '*.cc' -print | sort | sed "s,^$PREFIX/,," | tr "\n" " "` + +set +f # re-enable globbing + +# The sources consist of the portable files, plus the platform-specific port +# file. +echo "SOURCES=$PORTABLE_FILES $PORT_FILE $PORT_SSE_FILE" >> $OUTPUT +echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT + +if [ "$CROSS_COMPILE" = "true" ]; then + # Cross-compiling; do not try any compilation tests. + true +else + CXXOUTPUT="${TMPDIR}/leveldb_build_detect_platform-cxx.$$" + + # If -std=c++0x works, use as fallback for when memory barriers + # are not available. + $CXX $CXXFLAGS -std=c++0x -x c++ - -o $CXXOUTPUT 2>/dev/null < + int main() {} +EOF + if [ "$?" = 0 ]; then + COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX -DLEVELDB_ATOMIC_PRESENT" + PLATFORM_CXXFLAGS="-std=c++0x" + else + COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX" + fi + + # Test whether Snappy library is installed + # http://code.google.com/p/snappy/ + $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT 2>/dev/null < + int main() {} +EOF + if [ "$?" = 0 ]; then + COMMON_FLAGS="$COMMON_FLAGS -DSNAPPY" + PLATFORM_LIBS="$PLATFORM_LIBS -lsnappy" + fi + + # Test whether tcmalloc is available + $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT -ltcmalloc 2>/dev/null </dev/null + + # Test if gcc SSE 4.2 is supported + $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT -msse4.2 2>/dev/null </dev/null +fi + +# Use the SSE 4.2 CRC32C intrinsics iff runtime checks indicate compiler supports them. +if [ -n "$PLATFORM_SSEFLAGS" ]; then + PLATFORM_SSEFLAGS="$PLATFORM_SSEFLAGS -DLEVELDB_PLATFORM_POSIX_SSE" +fi + +PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS" +PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS" + +echo "CC=$CC" >> $OUTPUT +echo "CXX=$CXX" >> $OUTPUT +echo "PLATFORM=$PLATFORM" >> $OUTPUT +echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT +echo "PLATFORM_LIBS=$PLATFORM_LIBS" >> $OUTPUT +echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT +echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT +echo "PLATFORM_SSEFLAGS=$PLATFORM_SSEFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT +echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_VERSIONED=$PLATFORM_SHARED_VERSIONED" >> $OUTPUT diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/autocompact_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/autocompact_test.cc new file mode 100644 index 0000000..d20a236 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/autocompact_test.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2013 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" +#include "db/db_impl.h" +#include "leveldb/cache.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class AutoCompactTest { + public: + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + AutoCompactTest() { + dbname_ = test::TmpDir() + "/autocompact_test"; + tiny_cache_ = NewLRUCache(100); + options_.block_cache = tiny_cache_; + DestroyDB(dbname_, options_); + options_.create_if_missing = true; + options_.compression = kNoCompression; + ASSERT_OK(DB::Open(options_, dbname_, &db_)); + } + + ~AutoCompactTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete tiny_cache_; + } + + std::string Key(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "key%06d", i); + return std::string(buf); + } + + uint64_t Size(const Slice& start, const Slice& limit) { + Range r(start, limit); + uint64_t size; + db_->GetApproximateSizes(&r, 1, &size); + return size; + } + + void DoReads(int n); +}; + +static const int kValueSize = 200 * 1024; +static const int kTotalSize = 100 * 1024 * 1024; +static const int kCount = kTotalSize / kValueSize; + +// Read through the first n keys repeatedly and check that they get +// compacted (verified by checking the size of the key space). +void AutoCompactTest::DoReads(int n) { + std::string value(kValueSize, 'x'); + DBImpl* dbi = reinterpret_cast(db_); + + // Fill database + for (int i = 0; i < kCount; i++) { + ASSERT_OK(db_->Put(WriteOptions(), Key(i), value)); + } + ASSERT_OK(dbi->TEST_CompactMemTable()); + + // Delete everything + for (int i = 0; i < kCount; i++) { + ASSERT_OK(db_->Delete(WriteOptions(), Key(i))); + } + ASSERT_OK(dbi->TEST_CompactMemTable()); + + // Get initial measurement of the space we will be reading. + const int64_t initial_size = Size(Key(0), Key(n)); + const int64_t initial_other_size = Size(Key(n), Key(kCount)); + + // Read until size drops significantly. + std::string limit_key = Key(n); + for (int read = 0; true; read++) { + ASSERT_LT(read, 100) << "Taking too long to compact"; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); + iter->Valid() && iter->key().ToString() < limit_key; + iter->Next()) { + // Drop data + } + delete iter; + // Wait a little bit to allow any triggered compactions to complete. + Env::Default()->SleepForMicroseconds(1000000); + uint64_t size = Size(Key(0), Key(n)); + fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n", + read+1, size/1048576.0, Size(Key(n), Key(kCount))/1048576.0); + if (size <= initial_size/10) { + break; + } + } + + // Verify that the size of the key space not touched by the reads + // is pretty much unchanged. + const int64_t final_other_size = Size(Key(n), Key(kCount)); + ASSERT_LE(final_other_size, initial_other_size + 1048576); + ASSERT_GE(final_other_size, initial_other_size/5 - 1048576); +} + +TEST(AutoCompactTest, ReadAll) { + DoReads(kCount); +} + +TEST(AutoCompactTest, ReadHalf) { + DoReads(kCount/2); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/builder.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/builder.cc new file mode 100644 index 0000000..f419882 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/builder.cc @@ -0,0 +1,88 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/builder.h" + +#include "db/filename.h" +#include "db/dbformat.h" +#include "db/table_cache.h" +#include "db/version_edit.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" + +namespace leveldb { + +Status BuildTable(const std::string& dbname, + Env* env, + const Options& options, + TableCache* table_cache, + Iterator* iter, + FileMetaData* meta) { + Status s; + meta->file_size = 0; + iter->SeekToFirst(); + + std::string fname = TableFileName(dbname, meta->number); + if (iter->Valid()) { + WritableFile* file; + s = env->NewWritableFile(fname, &file); + if (!s.ok()) { + return s; + } + + TableBuilder* builder = new TableBuilder(options, file); + meta->smallest.DecodeFrom(iter->key()); + for (; iter->Valid(); iter->Next()) { + Slice key = iter->key(); + meta->largest.DecodeFrom(key); + builder->Add(key, iter->value()); + } + + // Finish and check for builder errors + if (s.ok()) { + s = builder->Finish(); + if (s.ok()) { + meta->file_size = builder->FileSize(); + assert(meta->file_size > 0); + } + } else { + builder->Abandon(); + } + delete builder; + + // Finish and check for file errors + if (s.ok()) { + s = file->Sync(); + } + if (s.ok()) { + s = file->Close(); + } + delete file; + file = NULL; + + if (s.ok()) { + // Verify that the table is usable + Iterator* it = table_cache->NewIterator(ReadOptions(), + meta->number, + meta->file_size); + s = it->status(); + delete it; + } + } + + // Check for input iterator errors + if (!iter->status().ok()) { + s = iter->status(); + } + + if (s.ok() && meta->file_size > 0) { + // Keep it + } else { + env->DeleteFile(fname); + } + return s; +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/builder.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/builder.h new file mode 100644 index 0000000..62431fc --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/builder.h @@ -0,0 +1,34 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_BUILDER_H_ +#define STORAGE_LEVELDB_DB_BUILDER_H_ + +#include "leveldb/status.h" + +namespace leveldb { + +struct Options; +struct FileMetaData; + +class Env; +class Iterator; +class TableCache; +class VersionEdit; + +// Build a Table file from the contents of *iter. The generated file +// will be named according to meta->number. On success, the rest of +// *meta will be filled with metadata about the generated table. +// If no data is present in *iter, meta->file_size will be set to +// zero, and no Table file will be produced. +extern Status BuildTable(const std::string& dbname, + Env* env, + const Options& options, + TableCache* table_cache, + Iterator* iter, + FileMetaData* meta); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_BUILDER_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/c.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/c.cc new file mode 100644 index 0000000..08ff0ad --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/c.cc @@ -0,0 +1,595 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/c.h" + +#include +#include +#include "leveldb/cache.h" +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/iterator.h" +#include "leveldb/options.h" +#include "leveldb/status.h" +#include "leveldb/write_batch.h" + +using leveldb::Cache; +using leveldb::Comparator; +using leveldb::CompressionType; +using leveldb::DB; +using leveldb::Env; +using leveldb::FileLock; +using leveldb::FilterPolicy; +using leveldb::Iterator; +using leveldb::kMajorVersion; +using leveldb::kMinorVersion; +using leveldb::Logger; +using leveldb::NewBloomFilterPolicy; +using leveldb::NewLRUCache; +using leveldb::Options; +using leveldb::RandomAccessFile; +using leveldb::Range; +using leveldb::ReadOptions; +using leveldb::SequentialFile; +using leveldb::Slice; +using leveldb::Snapshot; +using leveldb::Status; +using leveldb::WritableFile; +using leveldb::WriteBatch; +using leveldb::WriteOptions; + +extern "C" { + +struct leveldb_t { DB* rep; }; +struct leveldb_iterator_t { Iterator* rep; }; +struct leveldb_writebatch_t { WriteBatch rep; }; +struct leveldb_snapshot_t { const Snapshot* rep; }; +struct leveldb_readoptions_t { ReadOptions rep; }; +struct leveldb_writeoptions_t { WriteOptions rep; }; +struct leveldb_options_t { Options rep; }; +struct leveldb_cache_t { Cache* rep; }; +struct leveldb_seqfile_t { SequentialFile* rep; }; +struct leveldb_randomfile_t { RandomAccessFile* rep; }; +struct leveldb_writablefile_t { WritableFile* rep; }; +struct leveldb_logger_t { Logger* rep; }; +struct leveldb_filelock_t { FileLock* rep; }; + +struct leveldb_comparator_t : public Comparator { + void* state_; + void (*destructor_)(void*); + int (*compare_)( + void*, + const char* a, size_t alen, + const char* b, size_t blen); + const char* (*name_)(void*); + + virtual ~leveldb_comparator_t() { + (*destructor_)(state_); + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return (*compare_)(state_, a.data(), a.size(), b.data(), b.size()); + } + + virtual const char* Name() const { + return (*name_)(state_); + } + + // No-ops since the C binding does not support key shortening methods. + virtual void FindShortestSeparator(std::string*, const Slice&) const { } + virtual void FindShortSuccessor(std::string* key) const { } +}; + +struct leveldb_filterpolicy_t : public FilterPolicy { + void* state_; + void (*destructor_)(void*); + const char* (*name_)(void*); + char* (*create_)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length); + unsigned char (*key_match_)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length); + + virtual ~leveldb_filterpolicy_t() { + (*destructor_)(state_); + } + + virtual const char* Name() const { + return (*name_)(state_); + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + std::vector key_pointers(n); + std::vector key_sizes(n); + for (int i = 0; i < n; i++) { + key_pointers[i] = keys[i].data(); + key_sizes[i] = keys[i].size(); + } + size_t len; + char* filter = (*create_)(state_, &key_pointers[0], &key_sizes[0], n, &len); + dst->append(filter, len); + free(filter); + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { + return (*key_match_)(state_, key.data(), key.size(), + filter.data(), filter.size()); + } +}; + +struct leveldb_env_t { + Env* rep; + bool is_default; +}; + +static bool SaveError(char** errptr, const Status& s) { + assert(errptr != NULL); + if (s.ok()) { + return false; + } else if (*errptr == NULL) { + *errptr = strdup(s.ToString().c_str()); + } else { + // TODO(sanjay): Merge with existing error? + free(*errptr); + *errptr = strdup(s.ToString().c_str()); + } + return true; +} + +static char* CopyString(const std::string& str) { + char* result = reinterpret_cast(malloc(sizeof(char) * str.size())); + memcpy(result, str.data(), sizeof(char) * str.size()); + return result; +} + +leveldb_t* leveldb_open( + const leveldb_options_t* options, + const char* name, + char** errptr) { + DB* db; + if (SaveError(errptr, DB::Open(options->rep, std::string(name), &db))) { + return NULL; + } + leveldb_t* result = new leveldb_t; + result->rep = db; + return result; +} + +void leveldb_close(leveldb_t* db) { + delete db->rep; + delete db; +} + +void leveldb_put( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + const char* val, size_t vallen, + char** errptr) { + SaveError(errptr, + db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen))); +} + +void leveldb_delete( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + char** errptr) { + SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen))); +} + + +void leveldb_write( + leveldb_t* db, + const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, + char** errptr) { + SaveError(errptr, db->rep->Write(options->rep, &batch->rep)); +} + +char* leveldb_get( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, size_t keylen, + size_t* vallen, + char** errptr) { + char* result = NULL; + std::string tmp; + Status s = db->rep->Get(options->rep, Slice(key, keylen), &tmp); + if (s.ok()) { + *vallen = tmp.size(); + result = CopyString(tmp); + } else { + *vallen = 0; + if (!s.IsNotFound()) { + SaveError(errptr, s); + } + } + return result; +} + +leveldb_iterator_t* leveldb_create_iterator( + leveldb_t* db, + const leveldb_readoptions_t* options) { + leveldb_iterator_t* result = new leveldb_iterator_t; + result->rep = db->rep->NewIterator(options->rep); + return result; +} + +const leveldb_snapshot_t* leveldb_create_snapshot( + leveldb_t* db) { + leveldb_snapshot_t* result = new leveldb_snapshot_t; + result->rep = db->rep->GetSnapshot(); + return result; +} + +void leveldb_release_snapshot( + leveldb_t* db, + const leveldb_snapshot_t* snapshot) { + db->rep->ReleaseSnapshot(snapshot->rep); + delete snapshot; +} + +char* leveldb_property_value( + leveldb_t* db, + const char* propname) { + std::string tmp; + if (db->rep->GetProperty(Slice(propname), &tmp)) { + // We use strdup() since we expect human readable output. + return strdup(tmp.c_str()); + } else { + return NULL; + } +} + +void leveldb_approximate_sizes( + leveldb_t* db, + int num_ranges, + const char* const* range_start_key, const size_t* range_start_key_len, + const char* const* range_limit_key, const size_t* range_limit_key_len, + uint64_t* sizes) { + Range* ranges = new Range[num_ranges]; + for (int i = 0; i < num_ranges; i++) { + ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]); + ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]); + } + db->rep->GetApproximateSizes(ranges, num_ranges, sizes); + delete[] ranges; +} + +void leveldb_compact_range( + leveldb_t* db, + const char* start_key, size_t start_key_len, + const char* limit_key, size_t limit_key_len) { + Slice a, b; + db->rep->CompactRange( + // Pass NULL Slice if corresponding "const char*" is NULL + (start_key ? (a = Slice(start_key, start_key_len), &a) : NULL), + (limit_key ? (b = Slice(limit_key, limit_key_len), &b) : NULL)); +} + +void leveldb_destroy_db( + const leveldb_options_t* options, + const char* name, + char** errptr) { + SaveError(errptr, DestroyDB(name, options->rep)); +} + +void leveldb_repair_db( + const leveldb_options_t* options, + const char* name, + char** errptr) { + SaveError(errptr, RepairDB(name, options->rep)); +} + +void leveldb_iter_destroy(leveldb_iterator_t* iter) { + delete iter->rep; + delete iter; +} + +unsigned char leveldb_iter_valid(const leveldb_iterator_t* iter) { + return iter->rep->Valid(); +} + +void leveldb_iter_seek_to_first(leveldb_iterator_t* iter) { + iter->rep->SeekToFirst(); +} + +void leveldb_iter_seek_to_last(leveldb_iterator_t* iter) { + iter->rep->SeekToLast(); +} + +void leveldb_iter_seek(leveldb_iterator_t* iter, const char* k, size_t klen) { + iter->rep->Seek(Slice(k, klen)); +} + +void leveldb_iter_next(leveldb_iterator_t* iter) { + iter->rep->Next(); +} + +void leveldb_iter_prev(leveldb_iterator_t* iter) { + iter->rep->Prev(); +} + +const char* leveldb_iter_key(const leveldb_iterator_t* iter, size_t* klen) { + Slice s = iter->rep->key(); + *klen = s.size(); + return s.data(); +} + +const char* leveldb_iter_value(const leveldb_iterator_t* iter, size_t* vlen) { + Slice s = iter->rep->value(); + *vlen = s.size(); + return s.data(); +} + +void leveldb_iter_get_error(const leveldb_iterator_t* iter, char** errptr) { + SaveError(errptr, iter->rep->status()); +} + +leveldb_writebatch_t* leveldb_writebatch_create() { + return new leveldb_writebatch_t; +} + +void leveldb_writebatch_destroy(leveldb_writebatch_t* b) { + delete b; +} + +void leveldb_writebatch_clear(leveldb_writebatch_t* b) { + b->rep.Clear(); +} + +void leveldb_writebatch_put( + leveldb_writebatch_t* b, + const char* key, size_t klen, + const char* val, size_t vlen) { + b->rep.Put(Slice(key, klen), Slice(val, vlen)); +} + +void leveldb_writebatch_delete( + leveldb_writebatch_t* b, + const char* key, size_t klen) { + b->rep.Delete(Slice(key, klen)); +} + +void leveldb_writebatch_iterate( + leveldb_writebatch_t* b, + void* state, + void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), + void (*deleted)(void*, const char* k, size_t klen)) { + class H : public WriteBatch::Handler { + public: + void* state_; + void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen); + void (*deleted_)(void*, const char* k, size_t klen); + virtual void Put(const Slice& key, const Slice& value) { + (*put_)(state_, key.data(), key.size(), value.data(), value.size()); + } + virtual void Delete(const Slice& key) { + (*deleted_)(state_, key.data(), key.size()); + } + }; + H handler; + handler.state_ = state; + handler.put_ = put; + handler.deleted_ = deleted; + b->rep.Iterate(&handler); +} + +leveldb_options_t* leveldb_options_create() { + return new leveldb_options_t; +} + +void leveldb_options_destroy(leveldb_options_t* options) { + delete options; +} + +void leveldb_options_set_comparator( + leveldb_options_t* opt, + leveldb_comparator_t* cmp) { + opt->rep.comparator = cmp; +} + +void leveldb_options_set_filter_policy( + leveldb_options_t* opt, + leveldb_filterpolicy_t* policy) { + opt->rep.filter_policy = policy; +} + +void leveldb_options_set_create_if_missing( + leveldb_options_t* opt, unsigned char v) { + opt->rep.create_if_missing = v; +} + +void leveldb_options_set_error_if_exists( + leveldb_options_t* opt, unsigned char v) { + opt->rep.error_if_exists = v; +} + +void leveldb_options_set_paranoid_checks( + leveldb_options_t* opt, unsigned char v) { + opt->rep.paranoid_checks = v; +} + +void leveldb_options_set_env(leveldb_options_t* opt, leveldb_env_t* env) { + opt->rep.env = (env ? env->rep : NULL); +} + +void leveldb_options_set_info_log(leveldb_options_t* opt, leveldb_logger_t* l) { + opt->rep.info_log = (l ? l->rep : NULL); +} + +void leveldb_options_set_write_buffer_size(leveldb_options_t* opt, size_t s) { + opt->rep.write_buffer_size = s; +} + +void leveldb_options_set_max_open_files(leveldb_options_t* opt, int n) { + opt->rep.max_open_files = n; +} + +void leveldb_options_set_cache(leveldb_options_t* opt, leveldb_cache_t* c) { + opt->rep.block_cache = c->rep; +} + +void leveldb_options_set_block_size(leveldb_options_t* opt, size_t s) { + opt->rep.block_size = s; +} + +void leveldb_options_set_block_restart_interval(leveldb_options_t* opt, int n) { + opt->rep.block_restart_interval = n; +} + +void leveldb_options_set_compression(leveldb_options_t* opt, int t) { + opt->rep.compression = static_cast(t); +} + +leveldb_comparator_t* leveldb_comparator_create( + void* state, + void (*destructor)(void*), + int (*compare)( + void*, + const char* a, size_t alen, + const char* b, size_t blen), + const char* (*name)(void*)) { + leveldb_comparator_t* result = new leveldb_comparator_t; + result->state_ = state; + result->destructor_ = destructor; + result->compare_ = compare; + result->name_ = name; + return result; +} + +void leveldb_comparator_destroy(leveldb_comparator_t* cmp) { + delete cmp; +} + +leveldb_filterpolicy_t* leveldb_filterpolicy_create( + void* state, + void (*destructor)(void*), + char* (*create_filter)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length), + unsigned char (*key_may_match)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length), + const char* (*name)(void*)) { + leveldb_filterpolicy_t* result = new leveldb_filterpolicy_t; + result->state_ = state; + result->destructor_ = destructor; + result->create_ = create_filter; + result->key_match_ = key_may_match; + result->name_ = name; + return result; +} + +void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t* filter) { + delete filter; +} + +leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) { + // Make a leveldb_filterpolicy_t, but override all of its methods so + // they delegate to a NewBloomFilterPolicy() instead of user + // supplied C functions. + struct Wrapper : public leveldb_filterpolicy_t { + const FilterPolicy* rep_; + ~Wrapper() { delete rep_; } + const char* Name() const { return rep_->Name(); } + void CreateFilter(const Slice* keys, int n, std::string* dst) const { + return rep_->CreateFilter(keys, n, dst); + } + bool KeyMayMatch(const Slice& key, const Slice& filter) const { + return rep_->KeyMayMatch(key, filter); + } + static void DoNothing(void*) { } + }; + Wrapper* wrapper = new Wrapper; + wrapper->rep_ = NewBloomFilterPolicy(bits_per_key); + wrapper->state_ = NULL; + wrapper->destructor_ = &Wrapper::DoNothing; + return wrapper; +} + +leveldb_readoptions_t* leveldb_readoptions_create() { + return new leveldb_readoptions_t; +} + +void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) { + delete opt; +} + +void leveldb_readoptions_set_verify_checksums( + leveldb_readoptions_t* opt, + unsigned char v) { + opt->rep.verify_checksums = v; +} + +void leveldb_readoptions_set_fill_cache( + leveldb_readoptions_t* opt, unsigned char v) { + opt->rep.fill_cache = v; +} + +void leveldb_readoptions_set_snapshot( + leveldb_readoptions_t* opt, + const leveldb_snapshot_t* snap) { + opt->rep.snapshot = (snap ? snap->rep : NULL); +} + +leveldb_writeoptions_t* leveldb_writeoptions_create() { + return new leveldb_writeoptions_t; +} + +void leveldb_writeoptions_destroy(leveldb_writeoptions_t* opt) { + delete opt; +} + +void leveldb_writeoptions_set_sync( + leveldb_writeoptions_t* opt, unsigned char v) { + opt->rep.sync = v; +} + +leveldb_cache_t* leveldb_cache_create_lru(size_t capacity) { + leveldb_cache_t* c = new leveldb_cache_t; + c->rep = NewLRUCache(capacity); + return c; +} + +void leveldb_cache_destroy(leveldb_cache_t* cache) { + delete cache->rep; + delete cache; +} + +leveldb_env_t* leveldb_create_default_env() { + leveldb_env_t* result = new leveldb_env_t; + result->rep = Env::Default(); + result->is_default = true; + return result; +} + +void leveldb_env_destroy(leveldb_env_t* env) { + if (!env->is_default) delete env->rep; + delete env; +} + +void leveldb_free(void* ptr) { + free(ptr); +} + +int leveldb_major_version() { + return kMajorVersion; +} + +int leveldb_minor_version() { + return kMinorVersion; +} + +} // end extern "C" diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/c_test.c b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/c_test.c new file mode 100644 index 0000000..7cd5ee0 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/c_test.c @@ -0,0 +1,390 @@ +/* Copyright (c) 2011 The LevelDB Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. See the AUTHORS file for names of contributors. */ + +#include "leveldb/c.h" + +#include +#include +#include +#include +#include +#include + +const char* phase = ""; +static char dbname[200]; + +static void StartPhase(const char* name) { + fprintf(stderr, "=== Test %s\n", name); + phase = name; +} + +static const char* GetTempDir(void) { + const char* ret = getenv("TEST_TMPDIR"); + if (ret == NULL || ret[0] == '\0') + ret = "/tmp"; + return ret; +} + +#define CheckNoError(err) \ + if ((err) != NULL) { \ + fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \ + abort(); \ + } + +#define CheckCondition(cond) \ + if (!(cond)) { \ + fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, #cond); \ + abort(); \ + } + +static void CheckEqual(const char* expected, const char* v, size_t n) { + if (expected == NULL && v == NULL) { + // ok + } else if (expected != NULL && v != NULL && n == strlen(expected) && + memcmp(expected, v, n) == 0) { + // ok + return; + } else { + fprintf(stderr, "%s: expected '%s', got '%s'\n", + phase, + (expected ? expected : "(null)"), + (v ? v : "(null")); + abort(); + } +} + +static void Free(char** ptr) { + if (*ptr) { + free(*ptr); + *ptr = NULL; + } +} + +static void CheckGet( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, + const char* expected) { + char* err = NULL; + size_t val_len; + char* val; + val = leveldb_get(db, options, key, strlen(key), &val_len, &err); + CheckNoError(err); + CheckEqual(expected, val, val_len); + Free(&val); +} + +static void CheckIter(leveldb_iterator_t* iter, + const char* key, const char* val) { + size_t len; + const char* str; + str = leveldb_iter_key(iter, &len); + CheckEqual(key, str, len); + str = leveldb_iter_value(iter, &len); + CheckEqual(val, str, len); +} + +// Callback from leveldb_writebatch_iterate() +static void CheckPut(void* ptr, + const char* k, size_t klen, + const char* v, size_t vlen) { + int* state = (int*) ptr; + CheckCondition(*state < 2); + switch (*state) { + case 0: + CheckEqual("bar", k, klen); + CheckEqual("b", v, vlen); + break; + case 1: + CheckEqual("box", k, klen); + CheckEqual("c", v, vlen); + break; + } + (*state)++; +} + +// Callback from leveldb_writebatch_iterate() +static void CheckDel(void* ptr, const char* k, size_t klen) { + int* state = (int*) ptr; + CheckCondition(*state == 2); + CheckEqual("bar", k, klen); + (*state)++; +} + +static void CmpDestroy(void* arg) { } + +static int CmpCompare(void* arg, const char* a, size_t alen, + const char* b, size_t blen) { + int n = (alen < blen) ? alen : blen; + int r = memcmp(a, b, n); + if (r == 0) { + if (alen < blen) r = -1; + else if (alen > blen) r = +1; + } + return r; +} + +static const char* CmpName(void* arg) { + return "foo"; +} + +// Custom filter policy +static unsigned char fake_filter_result = 1; +static void FilterDestroy(void* arg) { } +static const char* FilterName(void* arg) { + return "TestFilter"; +} +static char* FilterCreate( + void* arg, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length) { + *filter_length = 4; + char* result = malloc(4); + memcpy(result, "fake", 4); + return result; +} +unsigned char FilterKeyMatch( + void* arg, + const char* key, size_t length, + const char* filter, size_t filter_length) { + CheckCondition(filter_length == 4); + CheckCondition(memcmp(filter, "fake", 4) == 0); + return fake_filter_result; +} + +int main(int argc, char** argv) { + leveldb_t* db; + leveldb_comparator_t* cmp; + leveldb_cache_t* cache; + leveldb_env_t* env; + leveldb_options_t* options; + leveldb_readoptions_t* roptions; + leveldb_writeoptions_t* woptions; + char* err = NULL; + int run = -1; + + CheckCondition(leveldb_major_version() >= 1); + CheckCondition(leveldb_minor_version() >= 1); + + snprintf(dbname, sizeof(dbname), + "%s/leveldb_c_test-%d", + GetTempDir(), + ((int) geteuid())); + + StartPhase("create_objects"); + cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName); + env = leveldb_create_default_env(); + cache = leveldb_cache_create_lru(100000); + + options = leveldb_options_create(); + leveldb_options_set_comparator(options, cmp); + leveldb_options_set_error_if_exists(options, 1); + leveldb_options_set_cache(options, cache); + leveldb_options_set_env(options, env); + leveldb_options_set_info_log(options, NULL); + leveldb_options_set_write_buffer_size(options, 100000); + leveldb_options_set_paranoid_checks(options, 1); + leveldb_options_set_max_open_files(options, 10); + leveldb_options_set_block_size(options, 1024); + leveldb_options_set_block_restart_interval(options, 8); + leveldb_options_set_compression(options, leveldb_no_compression); + + roptions = leveldb_readoptions_create(); + leveldb_readoptions_set_verify_checksums(roptions, 1); + leveldb_readoptions_set_fill_cache(roptions, 0); + + woptions = leveldb_writeoptions_create(); + leveldb_writeoptions_set_sync(woptions, 1); + + StartPhase("destroy"); + leveldb_destroy_db(options, dbname, &err); + Free(&err); + + StartPhase("open_error"); + db = leveldb_open(options, dbname, &err); + CheckCondition(err != NULL); + Free(&err); + + StartPhase("leveldb_free"); + db = leveldb_open(options, dbname, &err); + CheckCondition(err != NULL); + leveldb_free(err); + err = NULL; + + StartPhase("open"); + leveldb_options_set_create_if_missing(options, 1); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", NULL); + + StartPhase("put"); + leveldb_put(db, woptions, "foo", 3, "hello", 5, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("compactall"); + leveldb_compact_range(db, NULL, 0, NULL, 0); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("compactrange"); + leveldb_compact_range(db, "a", 1, "z", 1); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("writebatch"); + { + leveldb_writebatch_t* wb = leveldb_writebatch_create(); + leveldb_writebatch_put(wb, "foo", 3, "a", 1); + leveldb_writebatch_clear(wb); + leveldb_writebatch_put(wb, "bar", 3, "b", 1); + leveldb_writebatch_put(wb, "box", 3, "c", 1); + leveldb_writebatch_delete(wb, "bar", 3); + leveldb_write(db, woptions, wb, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", "hello"); + CheckGet(db, roptions, "bar", NULL); + CheckGet(db, roptions, "box", "c"); + int pos = 0; + leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel); + CheckCondition(pos == 3); + leveldb_writebatch_destroy(wb); + } + + StartPhase("iter"); + { + leveldb_iterator_t* iter = leveldb_create_iterator(db, roptions); + CheckCondition(!leveldb_iter_valid(iter)); + leveldb_iter_seek_to_first(iter); + CheckCondition(leveldb_iter_valid(iter)); + CheckIter(iter, "box", "c"); + leveldb_iter_next(iter); + CheckIter(iter, "foo", "hello"); + leveldb_iter_prev(iter); + CheckIter(iter, "box", "c"); + leveldb_iter_prev(iter); + CheckCondition(!leveldb_iter_valid(iter)); + leveldb_iter_seek_to_last(iter); + CheckIter(iter, "foo", "hello"); + leveldb_iter_seek(iter, "b", 1); + CheckIter(iter, "box", "c"); + leveldb_iter_get_error(iter, &err); + CheckNoError(err); + leveldb_iter_destroy(iter); + } + + StartPhase("approximate_sizes"); + { + int i; + int n = 20000; + char keybuf[100]; + char valbuf[100]; + uint64_t sizes[2]; + const char* start[2] = { "a", "k00000000000000010000" }; + size_t start_len[2] = { 1, 21 }; + const char* limit[2] = { "k00000000000000010000", "z" }; + size_t limit_len[2] = { 21, 1 }; + leveldb_writeoptions_set_sync(woptions, 0); + for (i = 0; i < n; i++) { + snprintf(keybuf, sizeof(keybuf), "k%020d", i); + snprintf(valbuf, sizeof(valbuf), "v%020d", i); + leveldb_put(db, woptions, keybuf, strlen(keybuf), valbuf, strlen(valbuf), + &err); + CheckNoError(err); + } + leveldb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes); + CheckCondition(sizes[0] > 0); + CheckCondition(sizes[1] > 0); + } + + StartPhase("property"); + { + char* prop = leveldb_property_value(db, "nosuchprop"); + CheckCondition(prop == NULL); + prop = leveldb_property_value(db, "leveldb.stats"); + CheckCondition(prop != NULL); + Free(&prop); + } + + StartPhase("snapshot"); + { + const leveldb_snapshot_t* snap; + snap = leveldb_create_snapshot(db); + leveldb_delete(db, woptions, "foo", 3, &err); + CheckNoError(err); + leveldb_readoptions_set_snapshot(roptions, snap); + CheckGet(db, roptions, "foo", "hello"); + leveldb_readoptions_set_snapshot(roptions, NULL); + CheckGet(db, roptions, "foo", NULL); + leveldb_release_snapshot(db, snap); + } + + StartPhase("repair"); + { + leveldb_close(db); + leveldb_options_set_create_if_missing(options, 0); + leveldb_options_set_error_if_exists(options, 0); + leveldb_repair_db(options, dbname, &err); + CheckNoError(err); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", NULL); + CheckGet(db, roptions, "bar", NULL); + CheckGet(db, roptions, "box", "c"); + leveldb_options_set_create_if_missing(options, 1); + leveldb_options_set_error_if_exists(options, 1); + } + + StartPhase("filter"); + for (run = 0; run < 2; run++) { + // First run uses custom filter, second run uses bloom filter + CheckNoError(err); + leveldb_filterpolicy_t* policy; + if (run == 0) { + policy = leveldb_filterpolicy_create( + NULL, FilterDestroy, FilterCreate, FilterKeyMatch, FilterName); + } else { + policy = leveldb_filterpolicy_create_bloom(10); + } + + // Create new database + leveldb_close(db); + leveldb_destroy_db(options, dbname, &err); + leveldb_options_set_filter_policy(options, policy); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + leveldb_put(db, woptions, "foo", 3, "foovalue", 8, &err); + CheckNoError(err); + leveldb_put(db, woptions, "bar", 3, "barvalue", 8, &err); + CheckNoError(err); + leveldb_compact_range(db, NULL, 0, NULL, 0); + + fake_filter_result = 1; + CheckGet(db, roptions, "foo", "foovalue"); + CheckGet(db, roptions, "bar", "barvalue"); + if (phase == 0) { + // Must not find value when custom filter returns false + fake_filter_result = 0; + CheckGet(db, roptions, "foo", NULL); + CheckGet(db, roptions, "bar", NULL); + fake_filter_result = 1; + + CheckGet(db, roptions, "foo", "foovalue"); + CheckGet(db, roptions, "bar", "barvalue"); + } + leveldb_options_set_filter_policy(options, NULL); + leveldb_filterpolicy_destroy(policy); + } + + StartPhase("cleanup"); + leveldb_close(db); + leveldb_options_destroy(options); + leveldb_readoptions_destroy(roptions); + leveldb_writeoptions_destroy(woptions); + leveldb_cache_destroy(cache); + leveldb_comparator_destroy(cmp); + leveldb_env_destroy(env); + + fprintf(stderr, "PASS\n"); + return 0; +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/corruption_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/corruption_test.cc new file mode 100644 index 0000000..37a484d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/corruption_test.cc @@ -0,0 +1,374 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" + +#include +#include +#include +#include +#include "leveldb/cache.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/log_format.h" +#include "db/version_set.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static const int kValueSize = 1000; + +class CorruptionTest { + public: + test::ErrorEnv env_; + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + CorruptionTest() { + tiny_cache_ = NewLRUCache(100); + options_.env = &env_; + options_.block_cache = tiny_cache_; + dbname_ = test::TmpDir() + "/corruption_test"; + DestroyDB(dbname_, options_); + + db_ = NULL; + options_.create_if_missing = true; + Reopen(); + options_.create_if_missing = false; + } + + ~CorruptionTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete tiny_cache_; + } + + Status TryReopen() { + delete db_; + db_ = NULL; + return DB::Open(options_, dbname_, &db_); + } + + void Reopen() { + ASSERT_OK(TryReopen()); + } + + void RepairDB() { + delete db_; + db_ = NULL; + ASSERT_OK(::leveldb::RepairDB(dbname_, options_)); + } + + void Build(int n) { + std::string key_space, value_space; + WriteBatch batch; + for (int i = 0; i < n; i++) { + //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n); + Slice key = Key(i, &key_space); + batch.Clear(); + batch.Put(key, Value(i, &value_space)); + WriteOptions options; + // Corrupt() doesn't work without this sync on windows; stat reports 0 for + // the file size. + if (i == n - 1) { + options.sync = true; + } + ASSERT_OK(db_->Write(options, &batch)); + } + } + + void Check(int min_expected, int max_expected) { + int next_expected = 0; + int missed = 0; + int bad_keys = 0; + int bad_values = 0; + int correct = 0; + std::string value_space; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + uint64_t key; + Slice in(iter->key()); + if (in == "" || in == "~") { + // Ignore boundary keys. + continue; + } + if (!ConsumeDecimalNumber(&in, &key) || + !in.empty() || + key < next_expected) { + bad_keys++; + continue; + } + missed += (key - next_expected); + next_expected = key + 1; + if (iter->value() != Value(key, &value_space)) { + bad_values++; + } else { + correct++; + } + } + delete iter; + + fprintf(stderr, + "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n", + min_expected, max_expected, correct, bad_keys, bad_values, missed); + ASSERT_LE(min_expected, correct); + ASSERT_GE(max_expected, correct); + } + + void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) { + // Pick file to corrupt + std::vector filenames; + ASSERT_OK(env_.GetChildren(dbname_, &filenames)); + uint64_t number; + FileType type; + std::string fname; + int picked_number = -1; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && + type == filetype && + int(number) > picked_number) { // Pick latest file + fname = dbname_ + "/" + filenames[i]; + picked_number = number; + } + } + ASSERT_TRUE(!fname.empty()) << filetype; + + struct stat sbuf; + if (stat(fname.c_str(), &sbuf) != 0) { + const char* msg = strerror(errno); + ASSERT_TRUE(false) << fname << ": " << msg; + } + + if (offset < 0) { + // Relative to end of file; make it absolute + if (-offset > sbuf.st_size) { + offset = 0; + } else { + offset = sbuf.st_size + offset; + } + } + if (offset > sbuf.st_size) { + offset = sbuf.st_size; + } + if (offset + bytes_to_corrupt > sbuf.st_size) { + bytes_to_corrupt = sbuf.st_size - offset; + } + + // Do it + std::string contents; + Status s = ReadFileToString(Env::Default(), fname, &contents); + ASSERT_TRUE(s.ok()) << s.ToString(); + for (int i = 0; i < bytes_to_corrupt; i++) { + contents[i + offset] ^= 0x80; + } + s = WriteStringToFile(Env::Default(), contents, fname); + ASSERT_TRUE(s.ok()) << s.ToString(); + } + + int Property(const std::string& name) { + std::string property; + int result; + if (db_->GetProperty(name, &property) && + sscanf(property.c_str(), "%d", &result) == 1) { + return result; + } else { + return -1; + } + } + + // Return the ith key + Slice Key(int i, std::string* storage) { + char buf[100]; + snprintf(buf, sizeof(buf), "%016d", i); + storage->assign(buf, strlen(buf)); + return Slice(*storage); + } + + // Return the value to associate with the specified key + Slice Value(int k, std::string* storage) { + Random r(k); + return test::RandomString(&r, kValueSize, storage); + } +}; + +TEST(CorruptionTest, Recovery) { + Build(100); + Check(100, 100); + Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record + Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block + Reopen(); + + // The 64 records in the first two log blocks are completely lost. + Check(36, 36); +} + +TEST(CorruptionTest, RecoverWriteError) { + env_.writable_file_error_ = true; + Status s = TryReopen(); + ASSERT_TRUE(!s.ok()); +} + +TEST(CorruptionTest, NewFileErrorDuringWrite) { + // Do enough writing to force minor compaction + env_.writable_file_error_ = true; + const int num = 3 + (Options().write_buffer_size / kValueSize); + std::string value_storage; + Status s; + for (int i = 0; s.ok() && i < num; i++) { + WriteBatch batch; + batch.Put("a", Value(100, &value_storage)); + s = db_->Write(WriteOptions(), &batch); + } + ASSERT_TRUE(!s.ok()); + ASSERT_GE(env_.num_writable_file_errors_, 1); + env_.writable_file_error_ = false; + Reopen(); +} + +TEST(CorruptionTest, TableFile) { + Build(100); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + dbi->TEST_CompactRange(1, NULL, NULL); + + Corrupt(kTableFile, 100, 1); + Check(90, 99); +} + +TEST(CorruptionTest, TableFileRepair) { + options_.block_size = 2 * kValueSize; // Limit scope of corruption + options_.paranoid_checks = true; + Reopen(); + Build(100); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + dbi->TEST_CompactRange(1, NULL, NULL); + + Corrupt(kTableFile, 100, 1); + RepairDB(); + Reopen(); + Check(95, 99); +} + +TEST(CorruptionTest, TableFileIndexData) { + Build(10000); // Enough to build multiple Tables + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + + Corrupt(kTableFile, -2000, 500); + Reopen(); + Check(5000, 9999); +} + +TEST(CorruptionTest, MissingDescriptor) { + Build(1000); + RepairDB(); + Reopen(); + Check(1000, 1000); +} + +TEST(CorruptionTest, SequenceNumberRecovery) { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5")); + RepairDB(); + Reopen(); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v5", v); + // Write something. If sequence number was not recovered properly, + // it will be hidden by an earlier write. + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6")); + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v6", v); + Reopen(); + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v6", v); +} + +TEST(CorruptionTest, CorruptedDescriptor) { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello")); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + + Corrupt(kDescriptorFile, 0, 1000); + Status s = TryReopen(); + ASSERT_TRUE(!s.ok()); + + RepairDB(); + Reopen(); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("hello", v); +} + +TEST(CorruptionTest, CompactionInputError) { + Build(10); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last))); + + Corrupt(kTableFile, 100, 1); + Check(5, 9); + + // Force compactions by writing lots of values + Build(10000); + Check(10000, 10000); +} + +TEST(CorruptionTest, CompactionInputErrorParanoid) { + options_.paranoid_checks = true; + options_.write_buffer_size = 512 << 10; + Reopen(); + DBImpl* dbi = reinterpret_cast(db_); + + // Make multiple inputs so we need to compact. + for (int i = 0; i < 2; i++) { + Build(10); + dbi->TEST_CompactMemTable(); + Corrupt(kTableFile, 100, 1); + env_.SleepForMicroseconds(100000); + } + dbi->CompactRange(NULL, NULL); + + // Write must fail because of corrupted table + std::string tmp1, tmp2; + Status s = db_->Put(WriteOptions(), Key(5, &tmp1), Value(5, &tmp2)); + ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db"; +} + +TEST(CorruptionTest, UnrelatedKeys) { + Build(10); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + Corrupt(kTableFile, 100, 1); + + std::string tmp1, tmp2; + ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2))); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v)); + ASSERT_EQ(Value(1000, &tmp2).ToString(), v); + dbi->TEST_CompactMemTable(); + ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v)); + ASSERT_EQ(Value(1000, &tmp2).ToString(), v); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_bench.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_bench.cc new file mode 100644 index 0000000..3ad19a5 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_bench.cc @@ -0,0 +1,1020 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include +#include "db/db_impl.h" +#include "db/version_set.h" +#include "leveldb/cache.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/write_batch.h" +#include "port/port.h" +#include "util/crc32c.h" +#include "util/histogram.h" +#include "util/mutexlock.h" +#include "util/random.h" +#include "util/testutil.h" + +// Comma-separated list of operations to run in the specified order +// Actual benchmarks: +// fillseq -- write N values in sequential key order in async mode +// fillrandom -- write N values in random key order in async mode +// overwrite -- overwrite N values in random key order in async mode +// fillsync -- write N/100 values in random key order in sync mode +// fill100K -- write N/1000 100K values in random order in async mode +// deleteseq -- delete N keys in sequential order +// deleterandom -- delete N keys in random order +// readseq -- read N times sequentially +// readreverse -- read N times in reverse order +// readrandom -- read N times in random order +// readmissing -- read N missing keys in random order +// readhot -- read N times in random order from 1% section of DB +// seekrandom -- N random seeks +// open -- cost of opening a DB +// crc32c -- repeated crc32c of 4K of data +// acquireload -- load N*1000 times +// Meta operations: +// compact -- Compact the entire DB +// stats -- Print DB stats +// sstables -- Print sstable info +// heapprofile -- Dump a heap profile (if supported by this port) +static const char* FLAGS_benchmarks = + "fillseq," + "fillsync," + "fillrandom," + "overwrite," + "readrandom," + "readrandom," // Extra run to allow previous compactions to quiesce + "readseq," + "readreverse," + "compact," + "readrandom," + "readseq," + "readreverse," + "fill100K," + "crc32c," + "snappycomp," + "snappyuncomp," + "acquireload," + ; + +// Number of key/values to place in database +static int FLAGS_num = 1000000; + +// Number of read operations to do. If negative, do FLAGS_num reads. +static int FLAGS_reads = -1; + +// Number of concurrent threads to run. +static int FLAGS_threads = 1; + +// Size of each value +static int FLAGS_value_size = 100; + +// Arrange to generate values that shrink to this fraction of +// their original size after compression +static double FLAGS_compression_ratio = 0.5; + +// Print histogram of operation timings +static bool FLAGS_histogram = false; + +// Number of bytes to buffer in memtable before compacting +// (initialized to default value by "main") +static int FLAGS_write_buffer_size = 0; + +// Number of bytes written to each file. +// (initialized to default value by "main") +static int FLAGS_max_file_size = 0; + +// Approximate size of user data packed per block (before compression. +// (initialized to default value by "main") +static int FLAGS_block_size = 0; + +// Number of bytes to use as a cache of uncompressed data. +// Negative means use default settings. +static int FLAGS_cache_size = -1; + +// Maximum number of files to keep open at the same time (use default if == 0) +static int FLAGS_open_files = 0; + +// Bloom filter bits per key. +// Negative means use default settings. +static int FLAGS_bloom_bits = -1; + +// If true, do not destroy the existing database. If you set this +// flag and also specify a benchmark that wants a fresh database, that +// benchmark will fail. +static bool FLAGS_use_existing_db = false; + +// If true, reuse existing log/MANIFEST files when re-opening a database. +static bool FLAGS_reuse_logs = false; + +// Use the db with the following name. +static const char* FLAGS_db = NULL; + +namespace leveldb { + +namespace { +leveldb::Env* g_env = NULL; + +// Helper for quickly generating random data. +class RandomGenerator { + private: + std::string data_; + int pos_; + + public: + RandomGenerator() { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < 1048576) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(size_t len) { + if (pos_ + len > data_.size()) { + pos_ = 0; + assert(len < data_.size()); + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } +}; + +#if defined(__linux) +static Slice TrimSpace(Slice s) { + size_t start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + size_t limit = s.size(); + while (limit > start && isspace(s[limit-1])) { + limit--; + } + return Slice(s.data() + start, limit - start); +} +#endif + +static void AppendWithSpace(std::string* str, Slice msg) { + if (msg.empty()) return; + if (!str->empty()) { + str->push_back(' '); + } + str->append(msg.data(), msg.size()); +} + +class Stats { + private: + double start_; + double finish_; + double seconds_; + int done_; + int next_report_; + int64_t bytes_; + double last_op_finish_; + Histogram hist_; + std::string message_; + + public: + Stats() { Start(); } + + void Start() { + next_report_ = 100; + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + bytes_ = 0; + seconds_ = 0; + start_ = g_env->NowMicros(); + finish_ = start_; + message_.clear(); + } + + void Merge(const Stats& other) { + hist_.Merge(other.hist_); + done_ += other.done_; + bytes_ += other.bytes_; + seconds_ += other.seconds_; + if (other.start_ < start_) start_ = other.start_; + if (other.finish_ > finish_) finish_ = other.finish_; + + // Just keep the messages from one thread + if (message_.empty()) message_ = other.message_; + } + + void Stop() { + finish_ = g_env->NowMicros(); + seconds_ = (finish_ - start_) * 1e-6; + } + + void AddMessage(Slice msg) { + AppendWithSpace(&message_, msg); + } + + void FinishedSingleOp() { + if (FLAGS_histogram) { + double now = g_env->NowMicros(); + double micros = now - last_op_finish_; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) next_report_ += 100; + else if (next_report_ < 5000) next_report_ += 500; + else if (next_report_ < 10000) next_report_ += 1000; + else if (next_report_ < 50000) next_report_ += 5000; + else if (next_report_ < 100000) next_report_ += 10000; + else if (next_report_ < 500000) next_report_ += 50000; + else next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void AddBytes(int64_t n) { + bytes_ += n; + } + + void Report(const Slice& name) { + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) done_ = 1; + + std::string extra; + if (bytes_ > 0) { + // Rate is computed on actual elapsed time, not the sum of per-thread + // elapsed times. + double elapsed = (finish_ - start_) * 1e-6; + char rate[100]; + snprintf(rate, sizeof(rate), "%6.1f MB/s", + (bytes_ / 1048576.0) / elapsed); + extra = rate; + } + AppendWithSpace(&extra, message_); + + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", + name.ToString().c_str(), + seconds_ * 1e6 / done_, + (extra.empty() ? "" : " "), + extra.c_str()); + if (FLAGS_histogram) { + fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); + } + fflush(stdout); + } +}; + +// State shared by all concurrent executions of the same benchmark. +struct SharedState { + port::Mutex mu; + port::CondVar cv; + int total; + + // Each thread goes through the following states: + // (1) initializing + // (2) waiting for others to be initialized + // (3) running + // (4) done + + int num_initialized; + int num_done; + bool start; + + SharedState() : cv(&mu) { } +}; + +// Per-thread state for concurrent executions of the same benchmark. +struct ThreadState { + int tid; // 0..n-1 when running in n threads + Random rand; // Has different seeds for different threads + Stats stats; + SharedState* shared; + + ThreadState(int index) + : tid(index), + rand(1000 + index) { + } +}; + +} // namespace + +class Benchmark { + private: + Cache* cache_; + const FilterPolicy* filter_policy_; + DB* db_; + int num_; + int value_size_; + int entries_per_batch_; + WriteOptions write_options_; + int reads_; + int heap_counter_; + + void PrintHeader() { + const int kKeySize = 16; + PrintEnvironment(); + fprintf(stdout, "Keys: %d bytes each\n", kKeySize); + fprintf(stdout, "Values: %d bytes each (%d bytes after compression)\n", + FLAGS_value_size, + static_cast(FLAGS_value_size * FLAGS_compression_ratio + 0.5)); + fprintf(stdout, "Entries: %d\n", num_); + fprintf(stdout, "RawSize: %.1f MB (estimated)\n", + ((static_cast(kKeySize + FLAGS_value_size) * num_) + / 1048576.0)); + fprintf(stdout, "FileSize: %.1f MB (estimated)\n", + (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) + / 1048576.0)); + PrintWarnings(); + fprintf(stdout, "------------------------------------------------\n"); + } + + void PrintWarnings() { +#if defined(__GNUC__) && !defined(__OPTIMIZE__) + fprintf(stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" + ); +#endif +#ifndef NDEBUG + fprintf(stdout, + "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); +#endif + + // See if snappy is working by attempting to compress a compressible string + const char text[] = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"; + std::string compressed; + if (!port::Snappy_Compress(text, sizeof(text), &compressed)) { + fprintf(stdout, "WARNING: Snappy compression is not enabled\n"); + } else if (compressed.size() >= sizeof(text)) { + fprintf(stdout, "WARNING: Snappy compression is not effective\n"); + } + } + + void PrintEnvironment() { + fprintf(stderr, "LevelDB: version %d.%d\n", + kMajorVersion, kMinorVersion); + +#if defined(__linux) + time_t now = time(NULL); + fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline + + FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char* sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str()); + fprintf(stderr, "CPUCache: %s\n", cache_size.c_str()); + } +#endif + } + + public: + Benchmark() + : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : NULL), + filter_policy_(FLAGS_bloom_bits >= 0 + ? NewBloomFilterPolicy(FLAGS_bloom_bits) + : NULL), + db_(NULL), + num_(FLAGS_num), + value_size_(FLAGS_value_size), + entries_per_batch_(1), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + heap_counter_(0) { + std::vector files; + g_env->GetChildren(FLAGS_db, &files); + for (size_t i = 0; i < files.size(); i++) { + if (Slice(files[i]).starts_with("heap-")) { + g_env->DeleteFile(std::string(FLAGS_db) + "/" + files[i]); + } + } + if (!FLAGS_use_existing_db) { + DestroyDB(FLAGS_db, Options()); + } + } + + ~Benchmark() { + delete db_; + delete cache_; + delete filter_policy_; + } + + void Run() { + PrintHeader(); + Open(); + + const char* benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char* sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + + // Reset parameters that may be overridden below + num_ = FLAGS_num; + reads_ = (FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads); + value_size_ = FLAGS_value_size; + entries_per_batch_ = 1; + write_options_ = WriteOptions(); + + void (Benchmark::*method)(ThreadState*) = NULL; + bool fresh_db = false; + int num_threads = FLAGS_threads; + + if (name == Slice("open")) { + method = &Benchmark::OpenBench; + num_ /= 10000; + if (num_ < 1) num_ = 1; + } else if (name == Slice("fillseq")) { + fresh_db = true; + method = &Benchmark::WriteSeq; + } else if (name == Slice("fillbatch")) { + fresh_db = true; + entries_per_batch_ = 1000; + method = &Benchmark::WriteSeq; + } else if (name == Slice("fillrandom")) { + fresh_db = true; + method = &Benchmark::WriteRandom; + } else if (name == Slice("overwrite")) { + fresh_db = false; + method = &Benchmark::WriteRandom; + } else if (name == Slice("fillsync")) { + fresh_db = true; + num_ /= 1000; + write_options_.sync = true; + method = &Benchmark::WriteRandom; + } else if (name == Slice("fill100K")) { + fresh_db = true; + num_ /= 1000; + value_size_ = 100 * 1000; + method = &Benchmark::WriteRandom; + } else if (name == Slice("readseq")) { + method = &Benchmark::ReadSequential; + } else if (name == Slice("readreverse")) { + method = &Benchmark::ReadReverse; + } else if (name == Slice("readrandom")) { + method = &Benchmark::ReadRandom; + } else if (name == Slice("readmissing")) { + method = &Benchmark::ReadMissing; + } else if (name == Slice("seekrandom")) { + method = &Benchmark::SeekRandom; + } else if (name == Slice("readhot")) { + method = &Benchmark::ReadHot; + } else if (name == Slice("readrandomsmall")) { + reads_ /= 1000; + method = &Benchmark::ReadRandom; + } else if (name == Slice("deleteseq")) { + method = &Benchmark::DeleteSeq; + } else if (name == Slice("deleterandom")) { + method = &Benchmark::DeleteRandom; + } else if (name == Slice("readwhilewriting")) { + num_threads++; // Add extra thread for writing + method = &Benchmark::ReadWhileWriting; + } else if (name == Slice("compact")) { + method = &Benchmark::Compact; + } else if (name == Slice("crc32c")) { + method = &Benchmark::Crc32c; + } else if (name == Slice("acquireload")) { + method = &Benchmark::AcquireLoad; + } else if (name == Slice("snappycomp")) { + method = &Benchmark::SnappyCompress; + } else if (name == Slice("snappyuncomp")) { + method = &Benchmark::SnappyUncompress; + } else if (name == Slice("heapprofile")) { + HeapProfile(); + } else if (name == Slice("stats")) { + PrintStats("leveldb.stats"); + } else if (name == Slice("sstables")) { + PrintStats("leveldb.sstables"); + } else { + if (name != Slice()) { // No error message for empty name + fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); + } + } + + if (fresh_db) { + if (FLAGS_use_existing_db) { + fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n", + name.ToString().c_str()); + method = NULL; + } else { + delete db_; + db_ = NULL; + DestroyDB(FLAGS_db, Options()); + Open(); + } + } + + if (method != NULL) { + RunBenchmark(num_threads, name, method); + } + } + } + + private: + struct ThreadArg { + Benchmark* bm; + SharedState* shared; + ThreadState* thread; + void (Benchmark::*method)(ThreadState*); + }; + + static void ThreadBody(void* v) { + ThreadArg* arg = reinterpret_cast(v); + SharedState* shared = arg->shared; + ThreadState* thread = arg->thread; + { + MutexLock l(&shared->mu); + shared->num_initialized++; + if (shared->num_initialized >= shared->total) { + shared->cv.SignalAll(); + } + while (!shared->start) { + shared->cv.Wait(); + } + } + + thread->stats.Start(); + (arg->bm->*(arg->method))(thread); + thread->stats.Stop(); + + { + MutexLock l(&shared->mu); + shared->num_done++; + if (shared->num_done >= shared->total) { + shared->cv.SignalAll(); + } + } + } + + void RunBenchmark(int n, Slice name, + void (Benchmark::*method)(ThreadState*)) { + SharedState shared; + shared.total = n; + shared.num_initialized = 0; + shared.num_done = 0; + shared.start = false; + + ThreadArg* arg = new ThreadArg[n]; + for (int i = 0; i < n; i++) { + arg[i].bm = this; + arg[i].method = method; + arg[i].shared = &shared; + arg[i].thread = new ThreadState(i); + arg[i].thread->shared = &shared; + g_env->StartThread(ThreadBody, &arg[i]); + } + + shared.mu.Lock(); + while (shared.num_initialized < n) { + shared.cv.Wait(); + } + + shared.start = true; + shared.cv.SignalAll(); + while (shared.num_done < n) { + shared.cv.Wait(); + } + shared.mu.Unlock(); + + for (int i = 1; i < n; i++) { + arg[0].thread->stats.Merge(arg[i].thread->stats); + } + arg[0].thread->stats.Report(name); + + for (int i = 0; i < n; i++) { + delete arg[i].thread; + } + delete[] arg; + } + + void Crc32c(ThreadState* thread) { + // Checksum about 500MB of data total + const int size = 4096; + const char* label = "(4K per op)"; + std::string data(size, 'x'); + int64_t bytes = 0; + uint32_t crc = 0; + while (bytes < 500 * 1048576) { + crc = crc32c::Value(data.data(), size); + thread->stats.FinishedSingleOp(); + bytes += size; + } + // Print so result is not dead + fprintf(stderr, "... crc=0x%x\r", static_cast(crc)); + + thread->stats.AddBytes(bytes); + thread->stats.AddMessage(label); + } + + void AcquireLoad(ThreadState* thread) { + int dummy; + port::AtomicPointer ap(&dummy); + int count = 0; + void *ptr = NULL; + thread->stats.AddMessage("(each op is 1000 loads)"); + while (count < 100000) { + for (int i = 0; i < 1000; i++) { + ptr = ap.Acquire_Load(); + } + count++; + thread->stats.FinishedSingleOp(); + } + if (ptr == NULL) exit(1); // Disable unused variable warning. + } + + void SnappyCompress(ThreadState* thread) { + RandomGenerator gen; + Slice input = gen.Generate(Options().block_size); + int64_t bytes = 0; + int64_t produced = 0; + bool ok = true; + std::string compressed; + while (ok && bytes < 1024 * 1048576) { // Compress 1G + ok = port::Snappy_Compress(input.data(), input.size(), &compressed); + produced += compressed.size(); + bytes += input.size(); + thread->stats.FinishedSingleOp(); + } + + if (!ok) { + thread->stats.AddMessage("(snappy failure)"); + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "(output: %.1f%%)", + (produced * 100.0) / bytes); + thread->stats.AddMessage(buf); + thread->stats.AddBytes(bytes); + } + } + + void SnappyUncompress(ThreadState* thread) { + RandomGenerator gen; + Slice input = gen.Generate(Options().block_size); + std::string compressed; + bool ok = port::Snappy_Compress(input.data(), input.size(), &compressed); + int64_t bytes = 0; + char* uncompressed = new char[input.size()]; + while (ok && bytes < 1024 * 1048576) { // Compress 1G + ok = port::Snappy_Uncompress(compressed.data(), compressed.size(), + uncompressed); + bytes += input.size(); + thread->stats.FinishedSingleOp(); + } + delete[] uncompressed; + + if (!ok) { + thread->stats.AddMessage("(snappy failure)"); + } else { + thread->stats.AddBytes(bytes); + } + } + + void Open() { + assert(db_ == NULL); + Options options; + options.env = g_env; + options.create_if_missing = !FLAGS_use_existing_db; + options.block_cache = cache_; + options.write_buffer_size = FLAGS_write_buffer_size; + options.max_file_size = FLAGS_max_file_size; + options.block_size = FLAGS_block_size; + options.max_open_files = FLAGS_open_files; + options.filter_policy = filter_policy_; + options.reuse_logs = FLAGS_reuse_logs; + Status s = DB::Open(options, FLAGS_db, &db_); + if (!s.ok()) { + fprintf(stderr, "open error: %s\n", s.ToString().c_str()); + exit(1); + } + } + + void OpenBench(ThreadState* thread) { + for (int i = 0; i < num_; i++) { + delete db_; + Open(); + thread->stats.FinishedSingleOp(); + } + } + + void WriteSeq(ThreadState* thread) { + DoWrite(thread, true); + } + + void WriteRandom(ThreadState* thread) { + DoWrite(thread, false); + } + + void DoWrite(ThreadState* thread, bool seq) { + if (num_ != FLAGS_num) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_); + thread->stats.AddMessage(msg); + } + + RandomGenerator gen; + WriteBatch batch; + Status s; + int64_t bytes = 0; + for (int i = 0; i < num_; i += entries_per_batch_) { + batch.Clear(); + for (int j = 0; j < entries_per_batch_; j++) { + const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + batch.Put(key, gen.Generate(value_size_)); + bytes += value_size_ + strlen(key); + thread->stats.FinishedSingleOp(); + } + s = db_->Write(write_options_, &batch); + if (!s.ok()) { + fprintf(stderr, "put error: %s\n", s.ToString().c_str()); + exit(1); + } + } + thread->stats.AddBytes(bytes); + } + + void ReadSequential(ThreadState* thread) { + Iterator* iter = db_->NewIterator(ReadOptions()); + int i = 0; + int64_t bytes = 0; + for (iter->SeekToFirst(); i < reads_ && iter->Valid(); iter->Next()) { + bytes += iter->key().size() + iter->value().size(); + thread->stats.FinishedSingleOp(); + ++i; + } + delete iter; + thread->stats.AddBytes(bytes); + } + + void ReadReverse(ThreadState* thread) { + Iterator* iter = db_->NewIterator(ReadOptions()); + int i = 0; + int64_t bytes = 0; + for (iter->SeekToLast(); i < reads_ && iter->Valid(); iter->Prev()) { + bytes += iter->key().size() + iter->value().size(); + thread->stats.FinishedSingleOp(); + ++i; + } + delete iter; + thread->stats.AddBytes(bytes); + } + + void ReadRandom(ThreadState* thread) { + ReadOptions options; + std::string value; + int found = 0; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d", k); + if (db_->Get(options, key, &value).ok()) { + found++; + } + thread->stats.FinishedSingleOp(); + } + char msg[100]; + snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_); + thread->stats.AddMessage(msg); + } + + void ReadMissing(ThreadState* thread) { + ReadOptions options; + std::string value; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d.", k); + db_->Get(options, key, &value); + thread->stats.FinishedSingleOp(); + } + } + + void ReadHot(ThreadState* thread) { + ReadOptions options; + std::string value; + const int range = (FLAGS_num + 99) / 100; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % range; + snprintf(key, sizeof(key), "%016d", k); + db_->Get(options, key, &value); + thread->stats.FinishedSingleOp(); + } + } + + void SeekRandom(ThreadState* thread) { + ReadOptions options; + int found = 0; + for (int i = 0; i < reads_; i++) { + Iterator* iter = db_->NewIterator(options); + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d", k); + iter->Seek(key); + if (iter->Valid() && iter->key() == key) found++; + delete iter; + thread->stats.FinishedSingleOp(); + } + char msg[100]; + snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_); + thread->stats.AddMessage(msg); + } + + void DoDelete(ThreadState* thread, bool seq) { + RandomGenerator gen; + WriteBatch batch; + Status s; + for (int i = 0; i < num_; i += entries_per_batch_) { + batch.Clear(); + for (int j = 0; j < entries_per_batch_; j++) { + const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + batch.Delete(key); + thread->stats.FinishedSingleOp(); + } + s = db_->Write(write_options_, &batch); + if (!s.ok()) { + fprintf(stderr, "del error: %s\n", s.ToString().c_str()); + exit(1); + } + } + } + + void DeleteSeq(ThreadState* thread) { + DoDelete(thread, true); + } + + void DeleteRandom(ThreadState* thread) { + DoDelete(thread, false); + } + + void ReadWhileWriting(ThreadState* thread) { + if (thread->tid > 0) { + ReadRandom(thread); + } else { + // Special thread that keeps writing until other threads are done. + RandomGenerator gen; + while (true) { + { + MutexLock l(&thread->shared->mu); + if (thread->shared->num_done + 1 >= thread->shared->num_initialized) { + // Other threads have finished + break; + } + } + + const int k = thread->rand.Next() % FLAGS_num; + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + Status s = db_->Put(write_options_, key, gen.Generate(value_size_)); + if (!s.ok()) { + fprintf(stderr, "put error: %s\n", s.ToString().c_str()); + exit(1); + } + } + + // Do not count any of the preceding work/delay in stats. + thread->stats.Start(); + } + } + + void Compact(ThreadState* thread) { + db_->CompactRange(NULL, NULL); + } + + void PrintStats(const char* key) { + std::string stats; + if (!db_->GetProperty(key, &stats)) { + stats = "(failed)"; + } + fprintf(stdout, "\n%s\n", stats.c_str()); + } + + static void WriteToFile(void* arg, const char* buf, int n) { + reinterpret_cast(arg)->Append(Slice(buf, n)); + } + + void HeapProfile() { + char fname[100]; + snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db, ++heap_counter_); + WritableFile* file; + Status s = g_env->NewWritableFile(fname, &file); + if (!s.ok()) { + fprintf(stderr, "%s\n", s.ToString().c_str()); + return; + } + bool ok = port::GetHeapProfile(WriteToFile, file); + delete file; + if (!ok) { + fprintf(stderr, "heap profiling not supported\n"); + g_env->DeleteFile(fname); + } + } +}; + +} // namespace leveldb + +int main(int argc, char** argv) { + FLAGS_write_buffer_size = leveldb::Options().write_buffer_size; + FLAGS_max_file_size = leveldb::Options().max_file_size; + FLAGS_block_size = leveldb::Options().block_size; + FLAGS_open_files = leveldb::Options().max_open_files; + std::string default_db_path; + + for (int i = 1; i < argc; i++) { + double d; + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) { + FLAGS_compression_ratio = d; + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_use_existing_db = n; + } else if (sscanf(argv[i], "--reuse_logs=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_reuse_logs = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--threads=%d%c", &n, &junk) == 1) { + FLAGS_threads = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) { + FLAGS_write_buffer_size = n; + } else if (sscanf(argv[i], "--max_file_size=%d%c", &n, &junk) == 1) { + FLAGS_max_file_size = n; + } else if (sscanf(argv[i], "--block_size=%d%c", &n, &junk) == 1) { + FLAGS_block_size = n; + } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) { + FLAGS_cache_size = n; + } else if (sscanf(argv[i], "--bloom_bits=%d%c", &n, &junk) == 1) { + FLAGS_bloom_bits = n; + } else if (sscanf(argv[i], "--open_files=%d%c", &n, &junk) == 1) { + FLAGS_open_files = n; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + leveldb::g_env = leveldb::Env::Default(); + + // Choose a location for the test database if none given with --db= + if (FLAGS_db == NULL) { + leveldb::g_env->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); + } + + leveldb::Benchmark benchmark; + benchmark.Run(); + return 0; +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_impl.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_impl.cc new file mode 100644 index 0000000..f43ad76 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_impl.cc @@ -0,0 +1,1568 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_impl.h" + +#include +#include +#include +#include +#include +#include +#include "db/builder.h" +#include "db/db_iter.h" +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/status.h" +#include "leveldb/table.h" +#include "leveldb/table_builder.h" +#include "port/port.h" +#include "table/block.h" +#include "table/merger.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" +#include "util/logging.h" +#include "util/mutexlock.h" + +namespace leveldb { + +const int kNumNonTableCacheFiles = 10; + +// Information kept for every waiting writer +struct DBImpl::Writer { + Status status; + WriteBatch* batch; + bool sync; + bool done; + port::CondVar cv; + + explicit Writer(port::Mutex* mu) : cv(mu) { } +}; + +struct DBImpl::CompactionState { + Compaction* const compaction; + + // Sequence numbers < smallest_snapshot are not significant since we + // will never have to service a snapshot below smallest_snapshot. + // Therefore if we have seen a sequence number S <= smallest_snapshot, + // we can drop all entries for the same key with sequence numbers < S. + SequenceNumber smallest_snapshot; + + // Files produced by compaction + struct Output { + uint64_t number; + uint64_t file_size; + InternalKey smallest, largest; + }; + std::vector outputs; + + // State kept for output being generated + WritableFile* outfile; + TableBuilder* builder; + + uint64_t total_bytes; + + Output* current_output() { return &outputs[outputs.size()-1]; } + + explicit CompactionState(Compaction* c) + : compaction(c), + outfile(NULL), + builder(NULL), + total_bytes(0) { + } +}; + +// Fix user-supplied options to be reasonable +template +static void ClipToRange(T* ptr, V minvalue, V maxvalue) { + if (static_cast(*ptr) > maxvalue) *ptr = maxvalue; + if (static_cast(*ptr) < minvalue) *ptr = minvalue; +} +Options SanitizeOptions(const std::string& dbname, + const InternalKeyComparator* icmp, + const InternalFilterPolicy* ipolicy, + const Options& src) { + Options result = src; + result.comparator = icmp; + result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL; + ClipToRange(&result.max_open_files, 64 + kNumNonTableCacheFiles, 50000); + ClipToRange(&result.write_buffer_size, 64<<10, 1<<30); + ClipToRange(&result.max_file_size, 1<<20, 1<<30); + ClipToRange(&result.block_size, 1<<10, 4<<20); + if (result.info_log == NULL) { + // Open a log file in the same directory as the db + src.env->CreateDir(dbname); // In case it does not exist + src.env->RenameFile(InfoLogFileName(dbname), OldInfoLogFileName(dbname)); + Status s = src.env->NewLogger(InfoLogFileName(dbname), &result.info_log); + if (!s.ok()) { + // No place suitable for logging + result.info_log = NULL; + } + } + if (result.block_cache == NULL) { + result.block_cache = NewLRUCache(8 << 20); + } + return result; +} + +DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) + : env_(raw_options.env), + internal_comparator_(raw_options.comparator), + internal_filter_policy_(raw_options.filter_policy), + options_(SanitizeOptions(dbname, &internal_comparator_, + &internal_filter_policy_, raw_options)), + owns_info_log_(options_.info_log != raw_options.info_log), + owns_cache_(options_.block_cache != raw_options.block_cache), + dbname_(dbname), + db_lock_(NULL), + shutting_down_(NULL), + bg_cv_(&mutex_), + mem_(NULL), + imm_(NULL), + logfile_(NULL), + logfile_number_(0), + log_(NULL), + seed_(0), + tmp_batch_(new WriteBatch), + bg_compaction_scheduled_(false), + manual_compaction_(NULL) { + has_imm_.Release_Store(NULL); + + // Reserve ten files or so for other uses and give the rest to TableCache. + const int table_cache_size = options_.max_open_files - kNumNonTableCacheFiles; + table_cache_ = new TableCache(dbname_, &options_, table_cache_size); + + versions_ = new VersionSet(dbname_, &options_, table_cache_, + &internal_comparator_); +} + +DBImpl::~DBImpl() { + // Wait for background work to finish + mutex_.Lock(); + shutting_down_.Release_Store(this); // Any non-NULL value is ok + while (bg_compaction_scheduled_) { + bg_cv_.Wait(); + } + mutex_.Unlock(); + + if (db_lock_ != NULL) { + env_->UnlockFile(db_lock_); + } + + delete versions_; + if (mem_ != NULL) mem_->Unref(); + if (imm_ != NULL) imm_->Unref(); + delete tmp_batch_; + delete log_; + delete logfile_; + delete table_cache_; + + if (owns_info_log_) { + delete options_.info_log; + } + if (owns_cache_) { + delete options_.block_cache; + } +} + +Status DBImpl::NewDB() { + VersionEdit new_db; + new_db.SetComparatorName(user_comparator()->Name()); + new_db.SetLogNumber(0); + new_db.SetNextFile(2); + new_db.SetLastSequence(0); + + const std::string manifest = DescriptorFileName(dbname_, 1); + WritableFile* file; + Status s = env_->NewWritableFile(manifest, &file); + if (!s.ok()) { + return s; + } + { + log::Writer log(file); + std::string record; + new_db.EncodeTo(&record); + s = log.AddRecord(record); + if (s.ok()) { + s = file->Close(); + } + } + delete file; + if (s.ok()) { + // Make "CURRENT" file that points to the new manifest file. + s = SetCurrentFile(env_, dbname_, 1); + } else { + env_->DeleteFile(manifest); + } + return s; +} + +void DBImpl::MaybeIgnoreError(Status* s) const { + if (s->ok() || options_.paranoid_checks) { + // No change needed + } else { + Log(options_.info_log, "Ignoring error %s", s->ToString().c_str()); + *s = Status::OK(); + } +} + +void DBImpl::DeleteObsoleteFiles() { + if (!bg_error_.ok()) { + // After a background error, we don't know whether a new version may + // or may not have been committed, so we cannot safely garbage collect. + return; + } + + // Make a set of all of the live files + std::set live = pending_outputs_; + versions_->AddLiveFiles(&live); + + std::vector filenames; + env_->GetChildren(dbname_, &filenames); // Ignoring errors on purpose + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + bool keep = true; + switch (type) { + case kLogFile: + keep = ((number >= versions_->LogNumber()) || + (number == versions_->PrevLogNumber())); + break; + case kDescriptorFile: + // Keep my manifest file, and any newer incarnations' + // (in case there is a race that allows other incarnations) + keep = (number >= versions_->ManifestFileNumber()); + break; + case kTableFile: + keep = (live.find(number) != live.end()); + break; + case kTempFile: + // Any temp files that are currently being written to must + // be recorded in pending_outputs_, which is inserted into "live" + keep = (live.find(number) != live.end()); + break; + case kCurrentFile: + case kDBLockFile: + case kInfoLogFile: + keep = true; + break; + } + + if (!keep) { + if (type == kTableFile) { + table_cache_->Evict(number); + } + Log(options_.info_log, "Delete type=%d #%lld\n", + int(type), + static_cast(number)); + env_->DeleteFile(dbname_ + "/" + filenames[i]); + } + } + } +} + +Status DBImpl::Recover(VersionEdit* edit, bool *save_manifest) { + mutex_.AssertHeld(); + + // Ignore error from CreateDir since the creation of the DB is + // committed only when the descriptor is created, and this directory + // may already exist from a previous failed creation attempt. + env_->CreateDir(dbname_); + assert(db_lock_ == NULL); + Status s = env_->LockFile(LockFileName(dbname_), &db_lock_); + if (!s.ok()) { + return s; + } + + if (!env_->FileExists(CurrentFileName(dbname_))) { + if (options_.create_if_missing) { + s = NewDB(); + if (!s.ok()) { + return s; + } + } else { + return Status::InvalidArgument( + dbname_, "does not exist (create_if_missing is false)"); + } + } else { + if (options_.error_if_exists) { + return Status::InvalidArgument( + dbname_, "exists (error_if_exists is true)"); + } + } + + s = versions_->Recover(save_manifest); + if (!s.ok()) { + return s; + } + SequenceNumber max_sequence(0); + + // Recover from all newer log files than the ones named in the + // descriptor (new log files may have been added by the previous + // incarnation without registering them in the descriptor). + // + // Note that PrevLogNumber() is no longer used, but we pay + // attention to it in case we are recovering a database + // produced by an older version of leveldb. + const uint64_t min_log = versions_->LogNumber(); + const uint64_t prev_log = versions_->PrevLogNumber(); + std::vector filenames; + s = env_->GetChildren(dbname_, &filenames); + if (!s.ok()) { + return s; + } + std::set expected; + versions_->AddLiveFiles(&expected); + uint64_t number; + FileType type; + std::vector logs; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + expected.erase(number); + if (type == kLogFile && ((number >= min_log) || (number == prev_log))) + logs.push_back(number); + } + } + if (!expected.empty()) { + char buf[50]; + snprintf(buf, sizeof(buf), "%d missing files; e.g.", + static_cast(expected.size())); + return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin()))); + } + + // Recover in the order in which the logs were generated + std::sort(logs.begin(), logs.end()); + for (size_t i = 0; i < logs.size(); i++) { + s = RecoverLogFile(logs[i], (i == logs.size() - 1), save_manifest, edit, + &max_sequence); + if (!s.ok()) { + return s; + } + + // The previous incarnation may not have written any MANIFEST + // records after allocating this log number. So we manually + // update the file number allocation counter in VersionSet. + versions_->MarkFileNumberUsed(logs[i]); + } + + if (versions_->LastSequence() < max_sequence) { + versions_->SetLastSequence(max_sequence); + } + + return Status::OK(); +} + +Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, + bool* save_manifest, VersionEdit* edit, + SequenceNumber* max_sequence) { + struct LogReporter : public log::Reader::Reporter { + Env* env; + Logger* info_log; + const char* fname; + Status* status; // NULL if options_.paranoid_checks==false + virtual void Corruption(size_t bytes, const Status& s) { + Log(info_log, "%s%s: dropping %d bytes; %s", + (this->status == NULL ? "(ignoring error) " : ""), + fname, static_cast(bytes), s.ToString().c_str()); + if (this->status != NULL && this->status->ok()) *this->status = s; + } + }; + + mutex_.AssertHeld(); + + // Open the log file + std::string fname = LogFileName(dbname_, log_number); + SequentialFile* file; + Status status = env_->NewSequentialFile(fname, &file); + if (!status.ok()) { + MaybeIgnoreError(&status); + return status; + } + + // Create the log reader. + LogReporter reporter; + reporter.env = env_; + reporter.info_log = options_.info_log; + reporter.fname = fname.c_str(); + reporter.status = (options_.paranoid_checks ? &status : NULL); + // We intentionally make log::Reader do checksumming even if + // paranoid_checks==false so that corruptions cause entire commits + // to be skipped instead of propagating bad information (like overly + // large sequence numbers). + log::Reader reader(file, &reporter, true/*checksum*/, + 0/*initial_offset*/); + Log(options_.info_log, "Recovering log #%llu", + (unsigned long long) log_number); + + // Read all the records and add to a memtable + std::string scratch; + Slice record; + WriteBatch batch; + int compactions = 0; + MemTable* mem = NULL; + while (reader.ReadRecord(&record, &scratch) && + status.ok()) { + if (record.size() < 12) { + reporter.Corruption( + record.size(), Status::Corruption("log record too small")); + continue; + } + WriteBatchInternal::SetContents(&batch, record); + + if (mem == NULL) { + mem = new MemTable(internal_comparator_); + mem->Ref(); + } + status = WriteBatchInternal::InsertInto(&batch, mem); + MaybeIgnoreError(&status); + if (!status.ok()) { + break; + } + const SequenceNumber last_seq = + WriteBatchInternal::Sequence(&batch) + + WriteBatchInternal::Count(&batch) - 1; + if (last_seq > *max_sequence) { + *max_sequence = last_seq; + } + + if (mem->ApproximateMemoryUsage() > options_.write_buffer_size) { + compactions++; + *save_manifest = true; + status = WriteLevel0Table(mem, edit, NULL); + mem->Unref(); + mem = NULL; + if (!status.ok()) { + // Reflect errors immediately so that conditions like full + // file-systems cause the DB::Open() to fail. + break; + } + } + } + + delete file; + + // See if we should keep reusing the last log file. + if (status.ok() && options_.reuse_logs && last_log && compactions == 0) { + assert(logfile_ == NULL); + assert(log_ == NULL); + assert(mem_ == NULL); + uint64_t lfile_size; + if (env_->GetFileSize(fname, &lfile_size).ok() && + env_->NewAppendableFile(fname, &logfile_).ok()) { + Log(options_.info_log, "Reusing old log %s \n", fname.c_str()); + log_ = new log::Writer(logfile_, lfile_size); + logfile_number_ = log_number; + if (mem != NULL) { + mem_ = mem; + mem = NULL; + } else { + // mem can be NULL if lognum exists but was empty. + mem_ = new MemTable(internal_comparator_); + mem_->Ref(); + } + } + } + + if (mem != NULL) { + // mem did not get reused; compact it. + if (status.ok()) { + *save_manifest = true; + status = WriteLevel0Table(mem, edit, NULL); + } + mem->Unref(); + } + + return status; +} + +Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, + Version* base) { + mutex_.AssertHeld(); + const uint64_t start_micros = env_->NowMicros(); + FileMetaData meta; + meta.number = versions_->NewFileNumber(); + pending_outputs_.insert(meta.number); + Iterator* iter = mem->NewIterator(); + Log(options_.info_log, "Level-0 table #%llu: started", + (unsigned long long) meta.number); + + Status s; + { + mutex_.Unlock(); + s = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta); + mutex_.Lock(); + } + + Log(options_.info_log, "Level-0 table #%llu: %lld bytes %s", + (unsigned long long) meta.number, + (unsigned long long) meta.file_size, + s.ToString().c_str()); + delete iter; + pending_outputs_.erase(meta.number); + + + // Note that if file_size is zero, the file has been deleted and + // should not be added to the manifest. + int level = 0; + if (s.ok() && meta.file_size > 0) { + const Slice min_user_key = meta.smallest.user_key(); + const Slice max_user_key = meta.largest.user_key(); + if (base != NULL) { + level = base->PickLevelForMemTableOutput(min_user_key, max_user_key); + } + edit->AddFile(level, meta.number, meta.file_size, + meta.smallest, meta.largest); + } + + CompactionStats stats; + stats.micros = env_->NowMicros() - start_micros; + stats.bytes_written = meta.file_size; + stats_[level].Add(stats); + return s; +} + +void DBImpl::CompactMemTable() { + mutex_.AssertHeld(); + assert(imm_ != NULL); + + // Save the contents of the memtable as a new Table + VersionEdit edit; + Version* base = versions_->current(); + base->Ref(); + Status s = WriteLevel0Table(imm_, &edit, base); + base->Unref(); + + if (s.ok() && shutting_down_.Acquire_Load()) { + s = Status::IOError("Deleting DB during memtable compaction"); + } + + // Replace immutable memtable with the generated Table + if (s.ok()) { + edit.SetPrevLogNumber(0); + edit.SetLogNumber(logfile_number_); // Earlier logs no longer needed + s = versions_->LogAndApply(&edit, &mutex_); + } + + if (s.ok()) { + // Commit to the new state + imm_->Unref(); + imm_ = NULL; + has_imm_.Release_Store(NULL); + DeleteObsoleteFiles(); + } else { + RecordBackgroundError(s); + } +} + +void DBImpl::CompactRange(const Slice* begin, const Slice* end) { + int max_level_with_files = 1; + { + MutexLock l(&mutex_); + Version* base = versions_->current(); + for (int level = 1; level < config::kNumLevels; level++) { + if (base->OverlapInLevel(level, begin, end)) { + max_level_with_files = level; + } + } + } + TEST_CompactMemTable(); // TODO(sanjay): Skip if memtable does not overlap + for (int level = 0; level < max_level_with_files; level++) { + TEST_CompactRange(level, begin, end); + } +} + +void DBImpl::TEST_CompactRange(int level, const Slice* begin,const Slice* end) { + assert(level >= 0); + assert(level + 1 < config::kNumLevels); + + InternalKey begin_storage, end_storage; + + ManualCompaction manual; + manual.level = level; + manual.done = false; + if (begin == NULL) { + manual.begin = NULL; + } else { + begin_storage = InternalKey(*begin, kMaxSequenceNumber, kValueTypeForSeek); + manual.begin = &begin_storage; + } + if (end == NULL) { + manual.end = NULL; + } else { + end_storage = InternalKey(*end, 0, static_cast(0)); + manual.end = &end_storage; + } + + MutexLock l(&mutex_); + while (!manual.done && !shutting_down_.Acquire_Load() && bg_error_.ok()) { + if (manual_compaction_ == NULL) { // Idle + manual_compaction_ = &manual; + MaybeScheduleCompaction(); + } else { // Running either my compaction or another compaction. + bg_cv_.Wait(); + } + } + if (manual_compaction_ == &manual) { + // Cancel my manual compaction since we aborted early for some reason. + manual_compaction_ = NULL; + } +} + +Status DBImpl::TEST_CompactMemTable() { + // NULL batch means just wait for earlier writes to be done + Status s = Write(WriteOptions(), NULL); + if (s.ok()) { + // Wait until the compaction completes + MutexLock l(&mutex_); + while (imm_ != NULL && bg_error_.ok()) { + bg_cv_.Wait(); + } + if (imm_ != NULL) { + s = bg_error_; + } + } + return s; +} + +void DBImpl::RecordBackgroundError(const Status& s) { + mutex_.AssertHeld(); + if (bg_error_.ok()) { + bg_error_ = s; + bg_cv_.SignalAll(); + } +} + +void DBImpl::MaybeScheduleCompaction() { + mutex_.AssertHeld(); + if (bg_compaction_scheduled_) { + // Already scheduled + } else if (shutting_down_.Acquire_Load()) { + // DB is being deleted; no more background compactions + } else if (!bg_error_.ok()) { + // Already got an error; no more changes + } else if (imm_ == NULL && + manual_compaction_ == NULL && + !versions_->NeedsCompaction()) { + // No work to be done + } else { + bg_compaction_scheduled_ = true; + env_->Schedule(&DBImpl::BGWork, this); + } +} + +void DBImpl::BGWork(void* db) { + reinterpret_cast(db)->BackgroundCall(); +} + +void DBImpl::BackgroundCall() { + MutexLock l(&mutex_); + assert(bg_compaction_scheduled_); + if (shutting_down_.Acquire_Load()) { + // No more background work when shutting down. + } else if (!bg_error_.ok()) { + // No more background work after a background error. + } else { + BackgroundCompaction(); + } + + bg_compaction_scheduled_ = false; + + // Previous compaction may have produced too many files in a level, + // so reschedule another compaction if needed. + MaybeScheduleCompaction(); + bg_cv_.SignalAll(); +} + +void DBImpl::BackgroundCompaction() { + mutex_.AssertHeld(); + + if (imm_ != NULL) { + CompactMemTable(); + return; + } + + Compaction* c; + bool is_manual = (manual_compaction_ != NULL); + InternalKey manual_end; + if (is_manual) { + ManualCompaction* m = manual_compaction_; + c = versions_->CompactRange(m->level, m->begin, m->end); + m->done = (c == NULL); + if (c != NULL) { + manual_end = c->input(0, c->num_input_files(0) - 1)->largest; + } + Log(options_.info_log, + "Manual compaction at level-%d from %s .. %s; will stop at %s\n", + m->level, + (m->begin ? m->begin->DebugString().c_str() : "(begin)"), + (m->end ? m->end->DebugString().c_str() : "(end)"), + (m->done ? "(end)" : manual_end.DebugString().c_str())); + } else { + c = versions_->PickCompaction(); + } + + Status status; + if (c == NULL) { + // Nothing to do + } else if (!is_manual && c->IsTrivialMove()) { + // Move file to next level + assert(c->num_input_files(0) == 1); + FileMetaData* f = c->input(0, 0); + c->edit()->DeleteFile(c->level(), f->number); + c->edit()->AddFile(c->level() + 1, f->number, f->file_size, + f->smallest, f->largest); + status = versions_->LogAndApply(c->edit(), &mutex_); + if (!status.ok()) { + RecordBackgroundError(status); + } + VersionSet::LevelSummaryStorage tmp; + Log(options_.info_log, "Moved #%lld to level-%d %lld bytes %s: %s\n", + static_cast(f->number), + c->level() + 1, + static_cast(f->file_size), + status.ToString().c_str(), + versions_->LevelSummary(&tmp)); + } else { + CompactionState* compact = new CompactionState(c); + status = DoCompactionWork(compact); + if (!status.ok()) { + RecordBackgroundError(status); + } + CleanupCompaction(compact); + c->ReleaseInputs(); + DeleteObsoleteFiles(); + } + delete c; + + if (status.ok()) { + // Done + } else if (shutting_down_.Acquire_Load()) { + // Ignore compaction errors found during shutting down + } else { + Log(options_.info_log, + "Compaction error: %s", status.ToString().c_str()); + } + + if (is_manual) { + ManualCompaction* m = manual_compaction_; + if (!status.ok()) { + m->done = true; + } + if (!m->done) { + // We only compacted part of the requested range. Update *m + // to the range that is left to be compacted. + m->tmp_storage = manual_end; + m->begin = &m->tmp_storage; + } + manual_compaction_ = NULL; + } +} + +void DBImpl::CleanupCompaction(CompactionState* compact) { + mutex_.AssertHeld(); + if (compact->builder != NULL) { + // May happen if we get a shutdown call in the middle of compaction + compact->builder->Abandon(); + delete compact->builder; + } else { + assert(compact->outfile == NULL); + } + delete compact->outfile; + for (size_t i = 0; i < compact->outputs.size(); i++) { + const CompactionState::Output& out = compact->outputs[i]; + pending_outputs_.erase(out.number); + } + delete compact; +} + +Status DBImpl::OpenCompactionOutputFile(CompactionState* compact) { + assert(compact != NULL); + assert(compact->builder == NULL); + uint64_t file_number; + { + mutex_.Lock(); + file_number = versions_->NewFileNumber(); + pending_outputs_.insert(file_number); + CompactionState::Output out; + out.number = file_number; + out.smallest.Clear(); + out.largest.Clear(); + compact->outputs.push_back(out); + mutex_.Unlock(); + } + + // Make the output file + std::string fname = TableFileName(dbname_, file_number); + Status s = env_->NewWritableFile(fname, &compact->outfile); + if (s.ok()) { + compact->builder = new TableBuilder(options_, compact->outfile); + } + return s; +} + +Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, + Iterator* input) { + assert(compact != NULL); + assert(compact->outfile != NULL); + assert(compact->builder != NULL); + + const uint64_t output_number = compact->current_output()->number; + assert(output_number != 0); + + // Check for iterator errors + Status s = input->status(); + const uint64_t current_entries = compact->builder->NumEntries(); + if (s.ok()) { + s = compact->builder->Finish(); + } else { + compact->builder->Abandon(); + } + const uint64_t current_bytes = compact->builder->FileSize(); + compact->current_output()->file_size = current_bytes; + compact->total_bytes += current_bytes; + delete compact->builder; + compact->builder = NULL; + + // Finish and check for file errors + if (s.ok()) { + s = compact->outfile->Sync(); + } + if (s.ok()) { + s = compact->outfile->Close(); + } + delete compact->outfile; + compact->outfile = NULL; + + if (s.ok() && current_entries > 0) { + // Verify that the table is usable + Iterator* iter = table_cache_->NewIterator(ReadOptions(), + output_number, + current_bytes); + s = iter->status(); + delete iter; + if (s.ok()) { + Log(options_.info_log, + "Generated table #%llu@%d: %lld keys, %lld bytes", + (unsigned long long) output_number, + compact->compaction->level(), + (unsigned long long) current_entries, + (unsigned long long) current_bytes); + } + } + return s; +} + + +Status DBImpl::InstallCompactionResults(CompactionState* compact) { + mutex_.AssertHeld(); + Log(options_.info_log, "Compacted %d@%d + %d@%d files => %lld bytes", + compact->compaction->num_input_files(0), + compact->compaction->level(), + compact->compaction->num_input_files(1), + compact->compaction->level() + 1, + static_cast(compact->total_bytes)); + + // Add compaction outputs + compact->compaction->AddInputDeletions(compact->compaction->edit()); + const int level = compact->compaction->level(); + for (size_t i = 0; i < compact->outputs.size(); i++) { + const CompactionState::Output& out = compact->outputs[i]; + compact->compaction->edit()->AddFile( + level + 1, + out.number, out.file_size, out.smallest, out.largest); + } + return versions_->LogAndApply(compact->compaction->edit(), &mutex_); +} + +Status DBImpl::DoCompactionWork(CompactionState* compact) { + const uint64_t start_micros = env_->NowMicros(); + int64_t imm_micros = 0; // Micros spent doing imm_ compactions + + Log(options_.info_log, "Compacting %d@%d + %d@%d files", + compact->compaction->num_input_files(0), + compact->compaction->level(), + compact->compaction->num_input_files(1), + compact->compaction->level() + 1); + + assert(versions_->NumLevelFiles(compact->compaction->level()) > 0); + assert(compact->builder == NULL); + assert(compact->outfile == NULL); + if (snapshots_.empty()) { + compact->smallest_snapshot = versions_->LastSequence(); + } else { + compact->smallest_snapshot = snapshots_.oldest()->number_; + } + + // Release mutex while we're actually doing the compaction work + mutex_.Unlock(); + + Iterator* input = versions_->MakeInputIterator(compact->compaction); + input->SeekToFirst(); + Status status; + ParsedInternalKey ikey; + std::string current_user_key; + bool has_current_user_key = false; + SequenceNumber last_sequence_for_key = kMaxSequenceNumber; + for (; input->Valid() && !shutting_down_.Acquire_Load(); ) { + // Prioritize immutable compaction work + if (has_imm_.NoBarrier_Load() != NULL) { + const uint64_t imm_start = env_->NowMicros(); + mutex_.Lock(); + if (imm_ != NULL) { + CompactMemTable(); + bg_cv_.SignalAll(); // Wakeup MakeRoomForWrite() if necessary + } + mutex_.Unlock(); + imm_micros += (env_->NowMicros() - imm_start); + } + + Slice key = input->key(); + if (compact->compaction->ShouldStopBefore(key) && + compact->builder != NULL) { + status = FinishCompactionOutputFile(compact, input); + if (!status.ok()) { + break; + } + } + + // Handle key/value, add to state, etc. + bool drop = false; + if (!ParseInternalKey(key, &ikey)) { + // Do not hide error keys + current_user_key.clear(); + has_current_user_key = false; + last_sequence_for_key = kMaxSequenceNumber; + } else { + if (!has_current_user_key || + user_comparator()->Compare(ikey.user_key, + Slice(current_user_key)) != 0) { + // First occurrence of this user key + current_user_key.assign(ikey.user_key.data(), ikey.user_key.size()); + has_current_user_key = true; + last_sequence_for_key = kMaxSequenceNumber; + } + + if (last_sequence_for_key <= compact->smallest_snapshot) { + // Hidden by an newer entry for same user key + drop = true; // (A) + } else if (ikey.type == kTypeDeletion && + ikey.sequence <= compact->smallest_snapshot && + compact->compaction->IsBaseLevelForKey(ikey.user_key)) { + // For this user key: + // (1) there is no data in higher levels + // (2) data in lower levels will have larger sequence numbers + // (3) data in layers that are being compacted here and have + // smaller sequence numbers will be dropped in the next + // few iterations of this loop (by rule (A) above). + // Therefore this deletion marker is obsolete and can be dropped. + drop = true; + } + + last_sequence_for_key = ikey.sequence; + } +#if 0 + Log(options_.info_log, + " Compact: %s, seq %d, type: %d %d, drop: %d, is_base: %d, " + "%d smallest_snapshot: %d", + ikey.user_key.ToString().c_str(), + (int)ikey.sequence, ikey.type, kTypeValue, drop, + compact->compaction->IsBaseLevelForKey(ikey.user_key), + (int)last_sequence_for_key, (int)compact->smallest_snapshot); +#endif + + if (!drop) { + // Open output file if necessary + if (compact->builder == NULL) { + status = OpenCompactionOutputFile(compact); + if (!status.ok()) { + break; + } + } + if (compact->builder->NumEntries() == 0) { + compact->current_output()->smallest.DecodeFrom(key); + } + compact->current_output()->largest.DecodeFrom(key); + compact->builder->Add(key, input->value()); + + // Close output file if it is big enough + if (compact->builder->FileSize() >= + compact->compaction->MaxOutputFileSize()) { + status = FinishCompactionOutputFile(compact, input); + if (!status.ok()) { + break; + } + } + } + + input->Next(); + } + + if (status.ok() && shutting_down_.Acquire_Load()) { + status = Status::IOError("Deleting DB during compaction"); + } + if (status.ok() && compact->builder != NULL) { + status = FinishCompactionOutputFile(compact, input); + } + if (status.ok()) { + status = input->status(); + } + delete input; + input = NULL; + + CompactionStats stats; + stats.micros = env_->NowMicros() - start_micros - imm_micros; + for (int which = 0; which < 2; which++) { + for (int i = 0; i < compact->compaction->num_input_files(which); i++) { + stats.bytes_read += compact->compaction->input(which, i)->file_size; + } + } + for (size_t i = 0; i < compact->outputs.size(); i++) { + stats.bytes_written += compact->outputs[i].file_size; + } + + mutex_.Lock(); + stats_[compact->compaction->level() + 1].Add(stats); + + if (status.ok()) { + status = InstallCompactionResults(compact); + } + if (!status.ok()) { + RecordBackgroundError(status); + } + VersionSet::LevelSummaryStorage tmp; + Log(options_.info_log, + "compacted to: %s", versions_->LevelSummary(&tmp)); + return status; +} + +namespace { +struct IterState { + port::Mutex* mu; + Version* version; + MemTable* mem; + MemTable* imm; +}; + +static void CleanupIteratorState(void* arg1, void* arg2) { + IterState* state = reinterpret_cast(arg1); + state->mu->Lock(); + state->mem->Unref(); + if (state->imm != NULL) state->imm->Unref(); + state->version->Unref(); + state->mu->Unlock(); + delete state; +} +} // namespace + +Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, + SequenceNumber* latest_snapshot, + uint32_t* seed) { + IterState* cleanup = new IterState; + mutex_.Lock(); + *latest_snapshot = versions_->LastSequence(); + + // Collect together all needed child iterators + std::vector list; + list.push_back(mem_->NewIterator()); + mem_->Ref(); + if (imm_ != NULL) { + list.push_back(imm_->NewIterator()); + imm_->Ref(); + } + versions_->current()->AddIterators(options, &list); + Iterator* internal_iter = + NewMergingIterator(&internal_comparator_, &list[0], list.size()); + versions_->current()->Ref(); + + cleanup->mu = &mutex_; + cleanup->mem = mem_; + cleanup->imm = imm_; + cleanup->version = versions_->current(); + internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL); + + *seed = ++seed_; + mutex_.Unlock(); + return internal_iter; +} + +Iterator* DBImpl::TEST_NewInternalIterator() { + SequenceNumber ignored; + uint32_t ignored_seed; + return NewInternalIterator(ReadOptions(), &ignored, &ignored_seed); +} + +int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes() { + MutexLock l(&mutex_); + return versions_->MaxNextLevelOverlappingBytes(); +} + +Status DBImpl::Get(const ReadOptions& options, + const Slice& key, + std::string* value) { + Status s; + MutexLock l(&mutex_); + SequenceNumber snapshot; + if (options.snapshot != NULL) { + snapshot = reinterpret_cast(options.snapshot)->number_; + } else { + snapshot = versions_->LastSequence(); + } + + MemTable* mem = mem_; + MemTable* imm = imm_; + Version* current = versions_->current(); + mem->Ref(); + if (imm != NULL) imm->Ref(); + current->Ref(); + + bool have_stat_update = false; + Version::GetStats stats; + + // Unlock while reading from files and memtables + { + mutex_.Unlock(); + // First look in the memtable, then in the immutable memtable (if any). + LookupKey lkey(key, snapshot); + if (mem->Get(lkey, value, &s)) { + // Done + } else if (imm != NULL && imm->Get(lkey, value, &s)) { + // Done + } else { + s = current->Get(options, lkey, value, &stats); + have_stat_update = true; + } + mutex_.Lock(); + } + + if (have_stat_update && current->UpdateStats(stats)) { + MaybeScheduleCompaction(); + } + mem->Unref(); + if (imm != NULL) imm->Unref(); + current->Unref(); + return s; +} + +Iterator* DBImpl::NewIterator(const ReadOptions& options) { + SequenceNumber latest_snapshot; + uint32_t seed; + Iterator* iter = NewInternalIterator(options, &latest_snapshot, &seed); + return NewDBIterator( + this, user_comparator(), iter, + (options.snapshot != NULL + ? reinterpret_cast(options.snapshot)->number_ + : latest_snapshot), + seed); +} + +void DBImpl::RecordReadSample(Slice key) { + MutexLock l(&mutex_); + if (versions_->current()->RecordReadSample(key)) { + MaybeScheduleCompaction(); + } +} + +const Snapshot* DBImpl::GetSnapshot() { + MutexLock l(&mutex_); + return snapshots_.New(versions_->LastSequence()); +} + +void DBImpl::ReleaseSnapshot(const Snapshot* s) { + MutexLock l(&mutex_); + snapshots_.Delete(reinterpret_cast(s)); +} + +// Convenience methods +Status DBImpl::Put(const WriteOptions& o, const Slice& key, const Slice& val) { + return DB::Put(o, key, val); +} + +Status DBImpl::Delete(const WriteOptions& options, const Slice& key) { + return DB::Delete(options, key); +} + +Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { + Writer w(&mutex_); + w.batch = my_batch; + w.sync = options.sync; + w.done = false; + + MutexLock l(&mutex_); + writers_.push_back(&w); + while (!w.done && &w != writers_.front()) { + w.cv.Wait(); + } + if (w.done) { + return w.status; + } + + // May temporarily unlock and wait. + Status status = MakeRoomForWrite(my_batch == NULL); + uint64_t last_sequence = versions_->LastSequence(); + Writer* last_writer = &w; + if (status.ok() && my_batch != NULL) { // NULL batch is for compactions + WriteBatch* updates = BuildBatchGroup(&last_writer); + WriteBatchInternal::SetSequence(updates, last_sequence + 1); + last_sequence += WriteBatchInternal::Count(updates); + + // Add to log and apply to memtable. We can release the lock + // during this phase since &w is currently responsible for logging + // and protects against concurrent loggers and concurrent writes + // into mem_. + { + mutex_.Unlock(); + status = log_->AddRecord(WriteBatchInternal::Contents(updates)); + bool sync_error = false; + if (status.ok() && options.sync) { + status = logfile_->Sync(); + if (!status.ok()) { + sync_error = true; + } + } + if (status.ok()) { + status = WriteBatchInternal::InsertInto(updates, mem_); + } + mutex_.Lock(); + if (sync_error) { + // The state of the log file is indeterminate: the log record we + // just added may or may not show up when the DB is re-opened. + // So we force the DB into a mode where all future writes fail. + RecordBackgroundError(status); + } + } + if (updates == tmp_batch_) tmp_batch_->Clear(); + + versions_->SetLastSequence(last_sequence); + } + + while (true) { + Writer* ready = writers_.front(); + writers_.pop_front(); + if (ready != &w) { + ready->status = status; + ready->done = true; + ready->cv.Signal(); + } + if (ready == last_writer) break; + } + + // Notify new head of write queue + if (!writers_.empty()) { + writers_.front()->cv.Signal(); + } + + return status; +} + +// REQUIRES: Writer list must be non-empty +// REQUIRES: First writer must have a non-NULL batch +WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) { + assert(!writers_.empty()); + Writer* first = writers_.front(); + WriteBatch* result = first->batch; + assert(result != NULL); + + size_t size = WriteBatchInternal::ByteSize(first->batch); + + // Allow the group to grow up to a maximum size, but if the + // original write is small, limit the growth so we do not slow + // down the small write too much. + size_t max_size = 1 << 20; + if (size <= (128<<10)) { + max_size = size + (128<<10); + } + + *last_writer = first; + std::deque::iterator iter = writers_.begin(); + ++iter; // Advance past "first" + for (; iter != writers_.end(); ++iter) { + Writer* w = *iter; + if (w->sync && !first->sync) { + // Do not include a sync write into a batch handled by a non-sync write. + break; + } + + if (w->batch != NULL) { + size += WriteBatchInternal::ByteSize(w->batch); + if (size > max_size) { + // Do not make batch too big + break; + } + + // Append to *result + if (result == first->batch) { + // Switch to temporary batch instead of disturbing caller's batch + result = tmp_batch_; + assert(WriteBatchInternal::Count(result) == 0); + WriteBatchInternal::Append(result, first->batch); + } + WriteBatchInternal::Append(result, w->batch); + } + *last_writer = w; + } + return result; +} + +// REQUIRES: mutex_ is held +// REQUIRES: this thread is currently at the front of the writer queue +Status DBImpl::MakeRoomForWrite(bool force) { + mutex_.AssertHeld(); + assert(!writers_.empty()); + bool allow_delay = !force; + Status s; + while (true) { + if (!bg_error_.ok()) { + // Yield previous error + s = bg_error_; + break; + } else if ( + allow_delay && + versions_->NumLevelFiles(0) >= config::kL0_SlowdownWritesTrigger) { + // We are getting close to hitting a hard limit on the number of + // L0 files. Rather than delaying a single write by several + // seconds when we hit the hard limit, start delaying each + // individual write by 1ms to reduce latency variance. Also, + // this delay hands over some CPU to the compaction thread in + // case it is sharing the same core as the writer. + mutex_.Unlock(); + env_->SleepForMicroseconds(1000); + allow_delay = false; // Do not delay a single write more than once + mutex_.Lock(); + } else if (!force && + (mem_->ApproximateMemoryUsage() <= options_.write_buffer_size)) { + // There is room in current memtable + break; + } else if (imm_ != NULL) { + // We have filled up the current memtable, but the previous + // one is still being compacted, so we wait. + Log(options_.info_log, "Current memtable full; waiting...\n"); + bg_cv_.Wait(); + } else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) { + // There are too many level-0 files. + Log(options_.info_log, "Too many L0 files; waiting...\n"); + bg_cv_.Wait(); + } else { + // Attempt to switch to a new memtable and trigger compaction of old + assert(versions_->PrevLogNumber() == 0); + uint64_t new_log_number = versions_->NewFileNumber(); + WritableFile* lfile = NULL; + s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile); + if (!s.ok()) { + // Avoid chewing through file number space in a tight loop. + versions_->ReuseFileNumber(new_log_number); + break; + } + delete log_; + delete logfile_; + logfile_ = lfile; + logfile_number_ = new_log_number; + log_ = new log::Writer(lfile); + imm_ = mem_; + has_imm_.Release_Store(imm_); + mem_ = new MemTable(internal_comparator_); + mem_->Ref(); + force = false; // Do not force another compaction if have room + MaybeScheduleCompaction(); + } + } + return s; +} + +bool DBImpl::GetProperty(const Slice& property, std::string* value) { + value->clear(); + + MutexLock l(&mutex_); + Slice in = property; + Slice prefix("leveldb."); + if (!in.starts_with(prefix)) return false; + in.remove_prefix(prefix.size()); + + if (in.starts_with("num-files-at-level")) { + in.remove_prefix(strlen("num-files-at-level")); + uint64_t level; + bool ok = ConsumeDecimalNumber(&in, &level) && in.empty(); + if (!ok || level >= config::kNumLevels) { + return false; + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "%d", + versions_->NumLevelFiles(static_cast(level))); + *value = buf; + return true; + } + } else if (in == "stats") { + char buf[200]; + snprintf(buf, sizeof(buf), + " Compactions\n" + "Level Files Size(MB) Time(sec) Read(MB) Write(MB)\n" + "--------------------------------------------------\n" + ); + value->append(buf); + for (int level = 0; level < config::kNumLevels; level++) { + int files = versions_->NumLevelFiles(level); + if (stats_[level].micros > 0 || files > 0) { + snprintf( + buf, sizeof(buf), + "%3d %8d %8.0f %9.0f %8.0f %9.0f\n", + level, + files, + versions_->NumLevelBytes(level) / 1048576.0, + stats_[level].micros / 1e6, + stats_[level].bytes_read / 1048576.0, + stats_[level].bytes_written / 1048576.0); + value->append(buf); + } + } + return true; + } else if (in == "sstables") { + *value = versions_->current()->DebugString(); + return true; + } else if (in == "approximate-memory-usage") { + size_t total_usage = options_.block_cache->TotalCharge(); + if (mem_) { + total_usage += mem_->ApproximateMemoryUsage(); + } + if (imm_) { + total_usage += imm_->ApproximateMemoryUsage(); + } + char buf[50]; + snprintf(buf, sizeof(buf), "%llu", + static_cast(total_usage)); + value->append(buf); + return true; + } + + return false; +} + +void DBImpl::GetApproximateSizes( + const Range* range, int n, + uint64_t* sizes) { + // TODO(opt): better implementation + Version* v; + { + MutexLock l(&mutex_); + versions_->current()->Ref(); + v = versions_->current(); + } + + for (int i = 0; i < n; i++) { + // Convert user_key into a corresponding internal key. + InternalKey k1(range[i].start, kMaxSequenceNumber, kValueTypeForSeek); + InternalKey k2(range[i].limit, kMaxSequenceNumber, kValueTypeForSeek); + uint64_t start = versions_->ApproximateOffsetOf(v, k1); + uint64_t limit = versions_->ApproximateOffsetOf(v, k2); + sizes[i] = (limit >= start ? limit - start : 0); + } + + { + MutexLock l(&mutex_); + v->Unref(); + } +} + +// Default implementations of convenience methods that subclasses of DB +// can call if they wish +Status DB::Put(const WriteOptions& opt, const Slice& key, const Slice& value) { + WriteBatch batch; + batch.Put(key, value); + return Write(opt, &batch); +} + +Status DB::Delete(const WriteOptions& opt, const Slice& key) { + WriteBatch batch; + batch.Delete(key); + return Write(opt, &batch); +} + +DB::~DB() { } + +Status DB::Open(const Options& options, const std::string& dbname, + DB** dbptr) { + *dbptr = NULL; + + DBImpl* impl = new DBImpl(options, dbname); + impl->mutex_.Lock(); + VersionEdit edit; + // Recover handles create_if_missing, error_if_exists + bool save_manifest = false; + Status s = impl->Recover(&edit, &save_manifest); + if (s.ok() && impl->mem_ == NULL) { + // Create new log and a corresponding memtable. + uint64_t new_log_number = impl->versions_->NewFileNumber(); + WritableFile* lfile; + s = options.env->NewWritableFile(LogFileName(dbname, new_log_number), + &lfile); + if (s.ok()) { + edit.SetLogNumber(new_log_number); + impl->logfile_ = lfile; + impl->logfile_number_ = new_log_number; + impl->log_ = new log::Writer(lfile); + impl->mem_ = new MemTable(impl->internal_comparator_); + impl->mem_->Ref(); + } + } + if (s.ok() && save_manifest) { + edit.SetPrevLogNumber(0); // No older logs needed after recovery. + edit.SetLogNumber(impl->logfile_number_); + s = impl->versions_->LogAndApply(&edit, &impl->mutex_); + } + if (s.ok()) { + impl->DeleteObsoleteFiles(); + impl->MaybeScheduleCompaction(); + } + impl->mutex_.Unlock(); + if (s.ok()) { + assert(impl->mem_ != NULL); + *dbptr = impl; + } else { + delete impl; + } + return s; +} + +Snapshot::~Snapshot() { +} + +Status DestroyDB(const std::string& dbname, const Options& options) { + Env* env = options.env; + std::vector filenames; + // Ignore error in case directory does not exist + env->GetChildren(dbname, &filenames); + if (filenames.empty()) { + return Status::OK(); + } + + FileLock* lock; + const std::string lockname = LockFileName(dbname); + Status result = env->LockFile(lockname, &lock); + if (result.ok()) { + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && + type != kDBLockFile) { // Lock file will be deleted at end + Status del = env->DeleteFile(dbname + "/" + filenames[i]); + if (result.ok() && !del.ok()) { + result = del; + } + } + } + env->UnlockFile(lock); // Ignore error since state is already gone + env->DeleteFile(lockname); + env->DeleteDir(dbname); // Ignore error in case dir contains other files + } + return result; +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_impl.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_impl.h new file mode 100644 index 0000000..8ff323e --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_impl.h @@ -0,0 +1,211 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_DB_IMPL_H_ +#define STORAGE_LEVELDB_DB_DB_IMPL_H_ + +#include +#include +#include "db/dbformat.h" +#include "db/log_writer.h" +#include "db/snapshot.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "port/port.h" +#include "port/thread_annotations.h" + +namespace leveldb { + +class MemTable; +class TableCache; +class Version; +class VersionEdit; +class VersionSet; + +class DBImpl : public DB { + public: + DBImpl(const Options& options, const std::string& dbname); + virtual ~DBImpl(); + + // Implementations of the DB interface + virtual Status Put(const WriteOptions&, const Slice& key, const Slice& value); + virtual Status Delete(const WriteOptions&, const Slice& key); + virtual Status Write(const WriteOptions& options, WriteBatch* updates); + virtual Status Get(const ReadOptions& options, + const Slice& key, + std::string* value); + virtual Iterator* NewIterator(const ReadOptions&); + virtual const Snapshot* GetSnapshot(); + virtual void ReleaseSnapshot(const Snapshot* snapshot); + virtual bool GetProperty(const Slice& property, std::string* value); + virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes); + virtual void CompactRange(const Slice* begin, const Slice* end); + + // Extra methods (for testing) that are not in the public DB interface + + // Compact any files in the named level that overlap [*begin,*end] + void TEST_CompactRange(int level, const Slice* begin, const Slice* end); + + // Force current memtable contents to be compacted. + Status TEST_CompactMemTable(); + + // Return an internal iterator over the current state of the database. + // The keys of this iterator are internal keys (see format.h). + // The returned iterator should be deleted when no longer needed. + Iterator* TEST_NewInternalIterator(); + + // Return the maximum overlapping data (in bytes) at next level for any + // file at a level >= 1. + int64_t TEST_MaxNextLevelOverlappingBytes(); + + // Record a sample of bytes read at the specified internal key. + // Samples are taken approximately once every config::kReadBytesPeriod + // bytes. + void RecordReadSample(Slice key); + + private: + friend class DB; + struct CompactionState; + struct Writer; + + Iterator* NewInternalIterator(const ReadOptions&, + SequenceNumber* latest_snapshot, + uint32_t* seed); + + Status NewDB(); + + // Recover the descriptor from persistent storage. May do a significant + // amount of work to recover recently logged updates. Any changes to + // be made to the descriptor are added to *edit. + Status Recover(VersionEdit* edit, bool* save_manifest) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + void MaybeIgnoreError(Status* s) const; + + // Delete any unneeded files and stale in-memory entries. + void DeleteObsoleteFiles(); + + // Compact the in-memory write buffer to disk. Switches to a new + // log-file/memtable and writes a new descriptor iff successful. + // Errors are recorded in bg_error_. + void CompactMemTable() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status RecoverLogFile(uint64_t log_number, bool last_log, bool* save_manifest, + VersionEdit* edit, SequenceNumber* max_sequence) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status MakeRoomForWrite(bool force /* compact even if there is room? */) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + WriteBatch* BuildBatchGroup(Writer** last_writer); + + void RecordBackgroundError(const Status& s); + + void MaybeScheduleCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + static void BGWork(void* db); + void BackgroundCall(); + void BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void CleanupCompaction(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + Status DoCompactionWork(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status OpenCompactionOutputFile(CompactionState* compact); + Status FinishCompactionOutputFile(CompactionState* compact, Iterator* input); + Status InstallCompactionResults(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Constant after construction + Env* const env_; + const InternalKeyComparator internal_comparator_; + const InternalFilterPolicy internal_filter_policy_; + const Options options_; // options_.comparator == &internal_comparator_ + bool owns_info_log_; + bool owns_cache_; + const std::string dbname_; + + // table_cache_ provides its own synchronization + TableCache* table_cache_; + + // Lock over the persistent DB state. Non-NULL iff successfully acquired. + FileLock* db_lock_; + + // State below is protected by mutex_ + port::Mutex mutex_; + port::AtomicPointer shutting_down_; + port::CondVar bg_cv_; // Signalled when background work finishes + MemTable* mem_; + MemTable* imm_; // Memtable being compacted + port::AtomicPointer has_imm_; // So bg thread can detect non-NULL imm_ + WritableFile* logfile_; + uint64_t logfile_number_; + log::Writer* log_; + uint32_t seed_; // For sampling. + + // Queue of writers. + std::deque writers_; + WriteBatch* tmp_batch_; + + SnapshotList snapshots_; + + // Set of table files to protect from deletion because they are + // part of ongoing compactions. + std::set pending_outputs_; + + // Has a background compaction been scheduled or is running? + bool bg_compaction_scheduled_; + + // Information for a manual compaction + struct ManualCompaction { + int level; + bool done; + const InternalKey* begin; // NULL means beginning of key range + const InternalKey* end; // NULL means end of key range + InternalKey tmp_storage; // Used to keep track of compaction progress + }; + ManualCompaction* manual_compaction_; + + VersionSet* versions_; + + // Have we encountered a background error in paranoid mode? + Status bg_error_; + + // Per level compaction stats. stats_[level] stores the stats for + // compactions that produced data for the specified "level". + struct CompactionStats { + int64_t micros; + int64_t bytes_read; + int64_t bytes_written; + + CompactionStats() : micros(0), bytes_read(0), bytes_written(0) { } + + void Add(const CompactionStats& c) { + this->micros += c.micros; + this->bytes_read += c.bytes_read; + this->bytes_written += c.bytes_written; + } + }; + CompactionStats stats_[config::kNumLevels]; + + // No copying allowed + DBImpl(const DBImpl&); + void operator=(const DBImpl&); + + const Comparator* user_comparator() const { + return internal_comparator_.user_comparator(); + } +}; + +// Sanitize db options. The caller should delete result.info_log if +// it is not equal to src.info_log. +extern Options SanitizeOptions(const std::string& db, + const InternalKeyComparator* icmp, + const InternalFilterPolicy* ipolicy, + const Options& src); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_DB_IMPL_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_iter.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_iter.cc new file mode 100644 index 0000000..3b2035e --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_iter.cc @@ -0,0 +1,317 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_iter.h" + +#include "db/filename.h" +#include "db/db_impl.h" +#include "db/dbformat.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/random.h" + +namespace leveldb { + +#if 0 +static void DumpInternalIter(Iterator* iter) { + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ParsedInternalKey k; + if (!ParseInternalKey(iter->key(), &k)) { + fprintf(stderr, "Corrupt '%s'\n", EscapeString(iter->key()).c_str()); + } else { + fprintf(stderr, "@ '%s'\n", k.DebugString().c_str()); + } + } +} +#endif + +namespace { + +// Memtables and sstables that make the DB representation contain +// (userkey,seq,type) => uservalue entries. DBIter +// combines multiple entries for the same userkey found in the DB +// representation into a single entry while accounting for sequence +// numbers, deletion markers, overwrites, etc. +class DBIter: public Iterator { + public: + // Which direction is the iterator currently moving? + // (1) When moving forward, the internal iterator is positioned at + // the exact entry that yields this->key(), this->value() + // (2) When moving backwards, the internal iterator is positioned + // just before all entries whose user key == this->key(). + enum Direction { + kForward, + kReverse + }; + + DBIter(DBImpl* db, const Comparator* cmp, Iterator* iter, SequenceNumber s, + uint32_t seed) + : db_(db), + user_comparator_(cmp), + iter_(iter), + sequence_(s), + direction_(kForward), + valid_(false), + rnd_(seed), + bytes_counter_(RandomPeriod()) { + } + virtual ~DBIter() { + delete iter_; + } + virtual bool Valid() const { return valid_; } + virtual Slice key() const { + assert(valid_); + return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_; + } + virtual Slice value() const { + assert(valid_); + return (direction_ == kForward) ? iter_->value() : saved_value_; + } + virtual Status status() const { + if (status_.ok()) { + return iter_->status(); + } else { + return status_; + } + } + + virtual void Next(); + virtual void Prev(); + virtual void Seek(const Slice& target); + virtual void SeekToFirst(); + virtual void SeekToLast(); + + private: + void FindNextUserEntry(bool skipping, std::string* skip); + void FindPrevUserEntry(); + bool ParseKey(ParsedInternalKey* key); + + inline void SaveKey(const Slice& k, std::string* dst) { + dst->assign(k.data(), k.size()); + } + + inline void ClearSavedValue() { + if (saved_value_.capacity() > 1048576) { + std::string empty; + swap(empty, saved_value_); + } else { + saved_value_.clear(); + } + } + + // Pick next gap with average value of config::kReadBytesPeriod. + ssize_t RandomPeriod() { + return rnd_.Uniform(2*config::kReadBytesPeriod); + } + + DBImpl* db_; + const Comparator* const user_comparator_; + Iterator* const iter_; + SequenceNumber const sequence_; + + Status status_; + std::string saved_key_; // == current key when direction_==kReverse + std::string saved_value_; // == current raw value when direction_==kReverse + Direction direction_; + bool valid_; + + Random rnd_; + ssize_t bytes_counter_; + + // No copying allowed + DBIter(const DBIter&); + void operator=(const DBIter&); +}; + +inline bool DBIter::ParseKey(ParsedInternalKey* ikey) { + Slice k = iter_->key(); + ssize_t n = k.size() + iter_->value().size(); + bytes_counter_ -= n; + while (bytes_counter_ < 0) { + bytes_counter_ += RandomPeriod(); + db_->RecordReadSample(k); + } + if (!ParseInternalKey(k, ikey)) { + status_ = Status::Corruption("corrupted internal key in DBIter"); + return false; + } else { + return true; + } +} + +void DBIter::Next() { + assert(valid_); + + if (direction_ == kReverse) { // Switch directions? + direction_ = kForward; + // iter_ is pointing just before the entries for this->key(), + // so advance into the range of entries for this->key() and then + // use the normal skipping code below. + if (!iter_->Valid()) { + iter_->SeekToFirst(); + } else { + iter_->Next(); + } + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + return; + } + // saved_key_ already contains the key to skip past. + } else { + // Store in saved_key_ the current key so we skip it below. + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + } + + FindNextUserEntry(true, &saved_key_); +} + +void DBIter::FindNextUserEntry(bool skipping, std::string* skip) { + // Loop until we hit an acceptable entry to yield + assert(iter_->Valid()); + assert(direction_ == kForward); + do { + ParsedInternalKey ikey; + if (ParseKey(&ikey) && ikey.sequence <= sequence_) { + switch (ikey.type) { + case kTypeDeletion: + // Arrange to skip all upcoming entries for this key since + // they are hidden by this deletion. + SaveKey(ikey.user_key, skip); + skipping = true; + break; + case kTypeValue: + if (skipping && + user_comparator_->Compare(ikey.user_key, *skip) <= 0) { + // Entry hidden + } else { + valid_ = true; + saved_key_.clear(); + return; + } + break; + } + } + iter_->Next(); + } while (iter_->Valid()); + saved_key_.clear(); + valid_ = false; +} + +void DBIter::Prev() { + assert(valid_); + + if (direction_ == kForward) { // Switch directions? + // iter_ is pointing at the current entry. Scan backwards until + // the key changes so we can use the normal reverse scanning code. + assert(iter_->Valid()); // Otherwise valid_ would have been false + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + while (true) { + iter_->Prev(); + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + ClearSavedValue(); + return; + } + if (user_comparator_->Compare(ExtractUserKey(iter_->key()), + saved_key_) < 0) { + break; + } + } + direction_ = kReverse; + } + + FindPrevUserEntry(); +} + +void DBIter::FindPrevUserEntry() { + assert(direction_ == kReverse); + + ValueType value_type = kTypeDeletion; + if (iter_->Valid()) { + do { + ParsedInternalKey ikey; + if (ParseKey(&ikey) && ikey.sequence <= sequence_) { + if ((value_type != kTypeDeletion) && + user_comparator_->Compare(ikey.user_key, saved_key_) < 0) { + // We encountered a non-deleted value in entries for previous keys, + break; + } + value_type = ikey.type; + if (value_type == kTypeDeletion) { + saved_key_.clear(); + ClearSavedValue(); + } else { + Slice raw_value = iter_->value(); + if (saved_value_.capacity() > raw_value.size() + 1048576) { + std::string empty; + swap(empty, saved_value_); + } + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + saved_value_.assign(raw_value.data(), raw_value.size()); + } + } + iter_->Prev(); + } while (iter_->Valid()); + } + + if (value_type == kTypeDeletion) { + // End + valid_ = false; + saved_key_.clear(); + ClearSavedValue(); + direction_ = kForward; + } else { + valid_ = true; + } +} + +void DBIter::Seek(const Slice& target) { + direction_ = kForward; + ClearSavedValue(); + saved_key_.clear(); + AppendInternalKey( + &saved_key_, ParsedInternalKey(target, sequence_, kValueTypeForSeek)); + iter_->Seek(saved_key_); + if (iter_->Valid()) { + FindNextUserEntry(false, &saved_key_ /* temporary storage */); + } else { + valid_ = false; + } +} + +void DBIter::SeekToFirst() { + direction_ = kForward; + ClearSavedValue(); + iter_->SeekToFirst(); + if (iter_->Valid()) { + FindNextUserEntry(false, &saved_key_ /* temporary storage */); + } else { + valid_ = false; + } +} + +void DBIter::SeekToLast() { + direction_ = kReverse; + ClearSavedValue(); + iter_->SeekToLast(); + FindPrevUserEntry(); +} + +} // anonymous namespace + +Iterator* NewDBIterator( + DBImpl* db, + const Comparator* user_key_comparator, + Iterator* internal_iter, + SequenceNumber sequence, + uint32_t seed) { + return new DBIter(db, user_key_comparator, internal_iter, sequence, seed); +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_iter.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_iter.h new file mode 100644 index 0000000..04927e9 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_iter.h @@ -0,0 +1,28 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_DB_ITER_H_ +#define STORAGE_LEVELDB_DB_DB_ITER_H_ + +#include +#include "leveldb/db.h" +#include "db/dbformat.h" + +namespace leveldb { + +class DBImpl; + +// Return a new iterator that converts internal keys (yielded by +// "*internal_iter") that were live at the specified "sequence" number +// into appropriate user keys. +extern Iterator* NewDBIterator( + DBImpl* db, + const Comparator* user_key_comparator, + Iterator* internal_iter, + SequenceNumber sequence, + uint32_t seed); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_DB_ITER_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_test.cc new file mode 100644 index 0000000..a0b08bc --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_test.cc @@ -0,0 +1,2158 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" +#include "leveldb/filter_policy.h" +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/cache.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "util/hash.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static std::string RandomString(Random* rnd, int len) { + std::string r; + test::RandomString(rnd, len, &r); + return r; +} + +namespace { +class AtomicCounter { + private: + port::Mutex mu_; + int count_; + public: + AtomicCounter() : count_(0) { } + void Increment() { + IncrementBy(1); + } + void IncrementBy(int count) { + MutexLock l(&mu_); + count_ += count; + } + int Read() { + MutexLock l(&mu_); + return count_; + } + void Reset() { + MutexLock l(&mu_); + count_ = 0; + } +}; + +void DelayMilliseconds(int millis) { + Env::Default()->SleepForMicroseconds(millis * 1000); +} +} + +// Special Env used to delay background operations +class SpecialEnv : public EnvWrapper { + public: + // sstable/log Sync() calls are blocked while this pointer is non-NULL. + port::AtomicPointer delay_data_sync_; + + // sstable/log Sync() calls return an error. + port::AtomicPointer data_sync_error_; + + // Simulate no-space errors while this pointer is non-NULL. + port::AtomicPointer no_space_; + + // Simulate non-writable file system while this pointer is non-NULL + port::AtomicPointer non_writable_; + + // Force sync of manifest files to fail while this pointer is non-NULL + port::AtomicPointer manifest_sync_error_; + + // Force write to manifest files to fail while this pointer is non-NULL + port::AtomicPointer manifest_write_error_; + + bool count_random_reads_; + AtomicCounter random_read_counter_; + + explicit SpecialEnv(Env* base) : EnvWrapper(base) { + delay_data_sync_.Release_Store(NULL); + data_sync_error_.Release_Store(NULL); + no_space_.Release_Store(NULL); + non_writable_.Release_Store(NULL); + count_random_reads_ = false; + manifest_sync_error_.Release_Store(NULL); + manifest_write_error_.Release_Store(NULL); + } + + Status NewWritableFile(const std::string& f, WritableFile** r) { + class DataFile : public WritableFile { + private: + SpecialEnv* env_; + WritableFile* base_; + + public: + DataFile(SpecialEnv* env, WritableFile* base) + : env_(env), + base_(base) { + } + ~DataFile() { delete base_; } + Status Append(const Slice& data) { + if (env_->no_space_.Acquire_Load() != NULL) { + // Drop writes on the floor + return Status::OK(); + } else { + return base_->Append(data); + } + } + Status Close() { return base_->Close(); } + Status Flush() { return base_->Flush(); } + Status Sync() { + if (env_->data_sync_error_.Acquire_Load() != NULL) { + return Status::IOError("simulated data sync error"); + } + while (env_->delay_data_sync_.Acquire_Load() != NULL) { + DelayMilliseconds(100); + } + return base_->Sync(); + } + }; + class ManifestFile : public WritableFile { + private: + SpecialEnv* env_; + WritableFile* base_; + public: + ManifestFile(SpecialEnv* env, WritableFile* b) : env_(env), base_(b) { } + ~ManifestFile() { delete base_; } + Status Append(const Slice& data) { + if (env_->manifest_write_error_.Acquire_Load() != NULL) { + return Status::IOError("simulated writer error"); + } else { + return base_->Append(data); + } + } + Status Close() { return base_->Close(); } + Status Flush() { return base_->Flush(); } + Status Sync() { + if (env_->manifest_sync_error_.Acquire_Load() != NULL) { + return Status::IOError("simulated sync error"); + } else { + return base_->Sync(); + } + } + }; + + if (non_writable_.Acquire_Load() != NULL) { + return Status::IOError("simulated write error"); + } + + Status s = target()->NewWritableFile(f, r); + if (s.ok()) { + if (strstr(f.c_str(), ".ldb") != NULL || + strstr(f.c_str(), ".log") != NULL) { + *r = new DataFile(this, *r); + } else if (strstr(f.c_str(), "MANIFEST") != NULL) { + *r = new ManifestFile(this, *r); + } + } + return s; + } + + Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { + class CountingFile : public RandomAccessFile { + private: + RandomAccessFile* target_; + AtomicCounter* counter_; + public: + CountingFile(RandomAccessFile* target, AtomicCounter* counter) + : target_(target), counter_(counter) { + } + virtual ~CountingFile() { delete target_; } + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + counter_->Increment(); + return target_->Read(offset, n, result, scratch); + } + }; + + Status s = target()->NewRandomAccessFile(f, r); + if (s.ok() && count_random_reads_) { + *r = new CountingFile(*r, &random_read_counter_); + } + return s; + } +}; + +class DBTest { + private: + const FilterPolicy* filter_policy_; + + // Sequence of option configurations to try + enum OptionConfig { + kDefault, + kReuse, + kFilter, + kUncompressed, + kEnd + }; + int option_config_; + + public: + std::string dbname_; + SpecialEnv* env_; + DB* db_; + + Options last_options_; + + DBTest() : option_config_(kDefault), + env_(new SpecialEnv(Env::Default())) { + filter_policy_ = NewBloomFilterPolicy(10); + dbname_ = test::TmpDir() + "/db_test"; + DestroyDB(dbname_, Options()); + db_ = NULL; + Reopen(); + } + + ~DBTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete env_; + delete filter_policy_; + } + + // Switch to a fresh database with the next option configuration to + // test. Return false if there are no more configurations to test. + bool ChangeOptions() { + option_config_++; + if (option_config_ >= kEnd) { + return false; + } else { + DestroyAndReopen(); + return true; + } + } + + // Return the current option configuration. + Options CurrentOptions() { + Options options; + options.reuse_logs = false; + switch (option_config_) { + case kReuse: + options.reuse_logs = true; + break; + case kFilter: + options.filter_policy = filter_policy_; + break; + case kUncompressed: + options.compression = kNoCompression; + break; + default: + break; + } + return options; + } + + DBImpl* dbfull() { + return reinterpret_cast(db_); + } + + void Reopen(Options* options = NULL) { + ASSERT_OK(TryReopen(options)); + } + + void Close() { + delete db_; + db_ = NULL; + } + + void DestroyAndReopen(Options* options = NULL) { + delete db_; + db_ = NULL; + DestroyDB(dbname_, Options()); + ASSERT_OK(TryReopen(options)); + } + + Status TryReopen(Options* options) { + delete db_; + db_ = NULL; + Options opts; + if (options != NULL) { + opts = *options; + } else { + opts = CurrentOptions(); + opts.create_if_missing = true; + } + last_options_ = opts; + + return DB::Open(opts, dbname_, &db_); + } + + Status Put(const std::string& k, const std::string& v) { + return db_->Put(WriteOptions(), k, v); + } + + Status Delete(const std::string& k) { + return db_->Delete(WriteOptions(), k); + } + + std::string Get(const std::string& k, const Snapshot* snapshot = NULL) { + ReadOptions options; + options.snapshot = snapshot; + std::string result; + Status s = db_->Get(options, k, &result); + if (s.IsNotFound()) { + result = "NOT_FOUND"; + } else if (!s.ok()) { + result = s.ToString(); + } + return result; + } + + // Return a string that contains all key,value pairs in order, + // formatted like "(k1->v1)(k2->v2)". + std::string Contents() { + std::vector forward; + std::string result; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + std::string s = IterStatus(iter); + result.push_back('('); + result.append(s); + result.push_back(')'); + forward.push_back(s); + } + + // Check reverse iteration results are the reverse of forward results + size_t matched = 0; + for (iter->SeekToLast(); iter->Valid(); iter->Prev()) { + ASSERT_LT(matched, forward.size()); + ASSERT_EQ(IterStatus(iter), forward[forward.size() - matched - 1]); + matched++; + } + ASSERT_EQ(matched, forward.size()); + + delete iter; + return result; + } + + std::string AllEntriesFor(const Slice& user_key) { + Iterator* iter = dbfull()->TEST_NewInternalIterator(); + InternalKey target(user_key, kMaxSequenceNumber, kTypeValue); + iter->Seek(target.Encode()); + std::string result; + if (!iter->status().ok()) { + result = iter->status().ToString(); + } else { + result = "[ "; + bool first = true; + while (iter->Valid()) { + ParsedInternalKey ikey; + if (!ParseInternalKey(iter->key(), &ikey)) { + result += "CORRUPTED"; + } else { + if (last_options_.comparator->Compare(ikey.user_key, user_key) != 0) { + break; + } + if (!first) { + result += ", "; + } + first = false; + switch (ikey.type) { + case kTypeValue: + result += iter->value().ToString(); + break; + case kTypeDeletion: + result += "DEL"; + break; + } + } + iter->Next(); + } + if (!first) { + result += " "; + } + result += "]"; + } + delete iter; + return result; + } + + int NumTableFilesAtLevel(int level) { + std::string property; + ASSERT_TRUE( + db_->GetProperty("leveldb.num-files-at-level" + NumberToString(level), + &property)); + return atoi(property.c_str()); + } + + int TotalTableFiles() { + int result = 0; + for (int level = 0; level < config::kNumLevels; level++) { + result += NumTableFilesAtLevel(level); + } + return result; + } + + // Return spread of files per level + std::string FilesPerLevel() { + std::string result; + int last_non_zero_offset = 0; + for (int level = 0; level < config::kNumLevels; level++) { + int f = NumTableFilesAtLevel(level); + char buf[100]; + snprintf(buf, sizeof(buf), "%s%d", (level ? "," : ""), f); + result += buf; + if (f > 0) { + last_non_zero_offset = result.size(); + } + } + result.resize(last_non_zero_offset); + return result; + } + + int CountFiles() { + std::vector files; + env_->GetChildren(dbname_, &files); + return static_cast(files.size()); + } + + uint64_t Size(const Slice& start, const Slice& limit) { + Range r(start, limit); + uint64_t size; + db_->GetApproximateSizes(&r, 1, &size); + return size; + } + + void Compact(const Slice& start, const Slice& limit) { + db_->CompactRange(&start, &limit); + } + + // Do n memtable compactions, each of which produces an sstable + // covering the range [small,large]. + void MakeTables(int n, const std::string& small, const std::string& large) { + for (int i = 0; i < n; i++) { + Put(small, "begin"); + Put(large, "end"); + dbfull()->TEST_CompactMemTable(); + } + } + + // Prevent pushing of new sstables into deeper levels by adding + // tables that cover a specified range to all levels. + void FillLevels(const std::string& smallest, const std::string& largest) { + MakeTables(config::kNumLevels, smallest, largest); + } + + void DumpFileCounts(const char* label) { + fprintf(stderr, "---\n%s:\n", label); + fprintf(stderr, "maxoverlap: %lld\n", + static_cast( + dbfull()->TEST_MaxNextLevelOverlappingBytes())); + for (int level = 0; level < config::kNumLevels; level++) { + int num = NumTableFilesAtLevel(level); + if (num > 0) { + fprintf(stderr, " level %3d : %d files\n", level, num); + } + } + } + + std::string DumpSSTableList() { + std::string property; + db_->GetProperty("leveldb.sstables", &property); + return property; + } + + std::string IterStatus(Iterator* iter) { + std::string result; + if (iter->Valid()) { + result = iter->key().ToString() + "->" + iter->value().ToString(); + } else { + result = "(invalid)"; + } + return result; + } + + bool DeleteAnSSTFile() { + std::vector filenames; + ASSERT_OK(env_->GetChildren(dbname_, &filenames)); + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && type == kTableFile) { + ASSERT_OK(env_->DeleteFile(TableFileName(dbname_, number))); + return true; + } + } + return false; + } + + // Returns number of files renamed. + int RenameLDBToSST() { + std::vector filenames; + ASSERT_OK(env_->GetChildren(dbname_, &filenames)); + uint64_t number; + FileType type; + int files_renamed = 0; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && type == kTableFile) { + const std::string from = TableFileName(dbname_, number); + const std::string to = SSTTableFileName(dbname_, number); + ASSERT_OK(env_->RenameFile(from, to)); + files_renamed++; + } + } + return files_renamed; + } +}; + +TEST(DBTest, Empty) { + do { + ASSERT_TRUE(db_ != NULL); + ASSERT_EQ("NOT_FOUND", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, ReadWrite) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(Put("bar", "v2")); + ASSERT_OK(Put("foo", "v3")); + ASSERT_EQ("v3", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + } while (ChangeOptions()); +} + +TEST(DBTest, PutDeleteGet) { + do { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2")); + ASSERT_EQ("v2", Get("foo")); + ASSERT_OK(db_->Delete(WriteOptions(), "foo")); + ASSERT_EQ("NOT_FOUND", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetFromImmutableLayer) { + do { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 100000; // Small write buffer + Reopen(&options); + + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + + env_->delay_data_sync_.Release_Store(env_); // Block sync calls + Put("k1", std::string(100000, 'x')); // Fill memtable + Put("k2", std::string(100000, 'y')); // Trigger compaction + ASSERT_EQ("v1", Get("foo")); + env_->delay_data_sync_.Release_Store(NULL); // Release sync calls + } while (ChangeOptions()); +} + +TEST(DBTest, GetFromVersions) { + do { + ASSERT_OK(Put("foo", "v1")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v1", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetMemUsage) { + do { + ASSERT_OK(Put("foo", "v1")); + std::string val; + ASSERT_TRUE(db_->GetProperty("leveldb.approximate-memory-usage", &val)); + int mem_usage = atoi(val.c_str()); + ASSERT_GT(mem_usage, 0); + ASSERT_LT(mem_usage, 5*1024*1024); + } while (ChangeOptions()); +} + +TEST(DBTest, GetSnapshot) { + do { + // Try with both a short key and a long key + for (int i = 0; i < 2; i++) { + std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x'); + ASSERT_OK(Put(key, "v1")); + const Snapshot* s1 = db_->GetSnapshot(); + ASSERT_OK(Put(key, "v2")); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s1)); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s1)); + db_->ReleaseSnapshot(s1); + } + } while (ChangeOptions()); +} + +TEST(DBTest, GetLevel0Ordering) { + do { + // Check that we process level-0 files in correct order. The code + // below generates two level-0 files where the earlier one comes + // before the later one in the level-0 file list since the earlier + // one has a smaller "smallest" key. + ASSERT_OK(Put("bar", "b")); + ASSERT_OK(Put("foo", "v1")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Put("foo", "v2")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetOrderedByLevels) { + do { + ASSERT_OK(Put("foo", "v1")); + Compact("a", "z"); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(Put("foo", "v2")); + ASSERT_EQ("v2", Get("foo")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetPicksCorrectFile) { + do { + // Arrange to have multiple files in a non-level-0 level. + ASSERT_OK(Put("a", "va")); + Compact("a", "b"); + ASSERT_OK(Put("x", "vx")); + Compact("x", "y"); + ASSERT_OK(Put("f", "vf")); + Compact("f", "g"); + ASSERT_EQ("va", Get("a")); + ASSERT_EQ("vf", Get("f")); + ASSERT_EQ("vx", Get("x")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetEncountersEmptyLevel) { + do { + // Arrange for the following to happen: + // * sstable A in level 0 + // * nothing in level 1 + // * sstable B in level 2 + // Then do enough Get() calls to arrange for an automatic compaction + // of sstable A. A bug would cause the compaction to be marked as + // occurring at level 1 (instead of the correct level 0). + + // Step 1: First place sstables in levels 0 and 2 + int compaction_count = 0; + while (NumTableFilesAtLevel(0) == 0 || + NumTableFilesAtLevel(2) == 0) { + ASSERT_LE(compaction_count, 100) << "could not fill levels 0 and 2"; + compaction_count++; + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + } + + // Step 2: clear level 1 if necessary. + dbfull()->TEST_CompactRange(1, NULL, NULL); + ASSERT_EQ(NumTableFilesAtLevel(0), 1); + ASSERT_EQ(NumTableFilesAtLevel(1), 0); + ASSERT_EQ(NumTableFilesAtLevel(2), 1); + + // Step 3: read a bunch of times + for (int i = 0; i < 1000; i++) { + ASSERT_EQ("NOT_FOUND", Get("missing")); + } + + // Step 4: Wait for compaction to finish + DelayMilliseconds(1000); + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + } while (ChangeOptions()); +} + +TEST(DBTest, IterEmpty) { + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("foo"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterSingle) { + ASSERT_OK(Put("a", "va")); + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek(""); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("a"); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("b"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterMulti) { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", "vb")); + ASSERT_OK(Put("c", "vc")); + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek(""); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Seek("a"); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Seek("ax"); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Seek("b"); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Seek("z"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + // Switch from reverse to forward + iter->SeekToLast(); + iter->Prev(); + iter->Prev(); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + + // Switch from forward to reverse + iter->SeekToFirst(); + iter->Next(); + iter->Next(); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + + // Make sure iter stays at snapshot + ASSERT_OK(Put("a", "va2")); + ASSERT_OK(Put("a2", "va3")); + ASSERT_OK(Put("b", "vb2")); + ASSERT_OK(Put("c", "vc2")); + ASSERT_OK(Delete("b")); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterSmallAndLargeMix) { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", std::string(100000, 'b'))); + ASSERT_OK(Put("c", "vc")); + ASSERT_OK(Put("d", std::string(100000, 'd'))); + ASSERT_OK(Put("e", std::string(100000, 'e'))); + + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterMultiWithDelete) { + do { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", "vb")); + ASSERT_OK(Put("c", "vc")); + ASSERT_OK(Delete("b")); + ASSERT_EQ("NOT_FOUND", Get("b")); + + Iterator* iter = db_->NewIterator(ReadOptions()); + iter->Seek("c"); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + delete iter; + } while (ChangeOptions()); +} + +TEST(DBTest, Recover) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_OK(Put("baz", "v5")); + + Reopen(); + ASSERT_EQ("v1", Get("foo")); + + ASSERT_EQ("v1", Get("foo")); + ASSERT_EQ("v5", Get("baz")); + ASSERT_OK(Put("bar", "v2")); + ASSERT_OK(Put("foo", "v3")); + + Reopen(); + ASSERT_EQ("v3", Get("foo")); + ASSERT_OK(Put("foo", "v4")); + ASSERT_EQ("v4", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + ASSERT_EQ("v5", Get("baz")); + } while (ChangeOptions()); +} + +TEST(DBTest, RecoveryWithEmptyLog) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_OK(Put("foo", "v2")); + Reopen(); + Reopen(); + ASSERT_OK(Put("foo", "v3")); + Reopen(); + ASSERT_EQ("v3", Get("foo")); + } while (ChangeOptions()); +} + +// Check that writes done during a memtable compaction are recovered +// if the database is shutdown during the memtable compaction. +TEST(DBTest, RecoverDuringMemtableCompaction) { + do { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 1000000; + Reopen(&options); + + // Trigger a long memtable compaction and reopen the database during it + ASSERT_OK(Put("foo", "v1")); // Goes to 1st log file + ASSERT_OK(Put("big1", std::string(10000000, 'x'))); // Fills memtable + ASSERT_OK(Put("big2", std::string(1000, 'y'))); // Triggers compaction + ASSERT_OK(Put("bar", "v2")); // Goes to new log file + + Reopen(&options); + ASSERT_EQ("v1", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + ASSERT_EQ(std::string(10000000, 'x'), Get("big1")); + ASSERT_EQ(std::string(1000, 'y'), Get("big2")); + } while (ChangeOptions()); +} + +static std::string Key(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "key%06d", i); + return std::string(buf); +} + +TEST(DBTest, MinorCompactionsHappen) { + Options options = CurrentOptions(); + options.write_buffer_size = 10000; + Reopen(&options); + + const int N = 500; + + int starting_num_tables = TotalTableFiles(); + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), Key(i) + std::string(1000, 'v'))); + } + int ending_num_tables = TotalTableFiles(); + ASSERT_GT(ending_num_tables, starting_num_tables); + + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i))); + } + + Reopen(); + + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i))); + } +} + +TEST(DBTest, RecoverWithLargeLog) { + { + Options options = CurrentOptions(); + Reopen(&options); + ASSERT_OK(Put("big1", std::string(200000, '1'))); + ASSERT_OK(Put("big2", std::string(200000, '2'))); + ASSERT_OK(Put("small3", std::string(10, '3'))); + ASSERT_OK(Put("small4", std::string(10, '4'))); + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + } + + // Make sure that if we re-open with a small write buffer size that + // we flush table files in the middle of a large log file. + Options options = CurrentOptions(); + options.write_buffer_size = 100000; + Reopen(&options); + ASSERT_EQ(NumTableFilesAtLevel(0), 3); + ASSERT_EQ(std::string(200000, '1'), Get("big1")); + ASSERT_EQ(std::string(200000, '2'), Get("big2")); + ASSERT_EQ(std::string(10, '3'), Get("small3")); + ASSERT_EQ(std::string(10, '4'), Get("small4")); + ASSERT_GT(NumTableFilesAtLevel(0), 1); +} + +TEST(DBTest, CompactionsGenerateMultipleFiles) { + Options options = CurrentOptions(); + options.write_buffer_size = 100000000; // Large write buffer + Reopen(&options); + + Random rnd(301); + + // Write 8MB (80 values, each 100K) + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + std::vector values; + for (int i = 0; i < 80; i++) { + values.push_back(RandomString(&rnd, 100000)); + ASSERT_OK(Put(Key(i), values[i])); + } + + // Reopening moves updates to level-0 + Reopen(&options); + dbfull()->TEST_CompactRange(0, NULL, NULL); + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GT(NumTableFilesAtLevel(1), 1); + for (int i = 0; i < 80; i++) { + ASSERT_EQ(Get(Key(i)), values[i]); + } +} + +TEST(DBTest, RepeatedWritesToSameKey) { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 100000; // Small write buffer + Reopen(&options); + + // We must have at most one file per level except for level-0, + // which may have up to kL0_StopWritesTrigger files. + const int kMaxFiles = config::kNumLevels + config::kL0_StopWritesTrigger; + + Random rnd(301); + std::string value = RandomString(&rnd, 2 * options.write_buffer_size); + for (int i = 0; i < 5 * kMaxFiles; i++) { + Put("key", value); + ASSERT_LE(TotalTableFiles(), kMaxFiles); + fprintf(stderr, "after %d: %d files\n", int(i+1), TotalTableFiles()); + } +} + +TEST(DBTest, SparseMerge) { + Options options = CurrentOptions(); + options.compression = kNoCompression; + Reopen(&options); + + FillLevels("A", "Z"); + + // Suppose there is: + // small amount of data with prefix A + // large amount of data with prefix B + // small amount of data with prefix C + // and that recent updates have made small changes to all three prefixes. + // Check that we do not do a compaction that merges all of B in one shot. + const std::string value(1000, 'x'); + Put("A", "va"); + // Write approximately 100MB of "B" values + for (int i = 0; i < 100000; i++) { + char key[100]; + snprintf(key, sizeof(key), "B%010d", i); + Put(key, value); + } + Put("C", "vc"); + dbfull()->TEST_CompactMemTable(); + dbfull()->TEST_CompactRange(0, NULL, NULL); + + // Make sparse update + Put("A", "va2"); + Put("B100", "bvalue2"); + Put("C", "vc2"); + dbfull()->TEST_CompactMemTable(); + + // Compactions should not cause us to create a situation where + // a file overlaps too much data at the next level. + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + dbfull()->TEST_CompactRange(0, NULL, NULL); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + dbfull()->TEST_CompactRange(1, NULL, NULL); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); +} + +static bool Between(uint64_t val, uint64_t low, uint64_t high) { + bool result = (val >= low) && (val <= high); + if (!result) { + fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", + (unsigned long long)(val), + (unsigned long long)(low), + (unsigned long long)(high)); + } + return result; +} + +TEST(DBTest, ApproximateSizes) { + do { + Options options = CurrentOptions(); + options.write_buffer_size = 100000000; // Large write buffer + options.compression = kNoCompression; + DestroyAndReopen(); + + ASSERT_TRUE(Between(Size("", "xyz"), 0, 0)); + Reopen(&options); + ASSERT_TRUE(Between(Size("", "xyz"), 0, 0)); + + // Write 8MB (80 values, each 100K) + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + const int N = 80; + static const int S1 = 100000; + static const int S2 = 105000; // Allow some expansion from metadata + Random rnd(301); + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), RandomString(&rnd, S1))); + } + + // 0 because GetApproximateSizes() does not account for memtable space + ASSERT_TRUE(Between(Size("", Key(50)), 0, 0)); + + if (options.reuse_logs) { + // Recovery will reuse memtable, and GetApproximateSizes() does not + // account for memtable usage; + Reopen(&options); + ASSERT_TRUE(Between(Size("", Key(50)), 0, 0)); + continue; + } + + // Check sizes across recovery by reopening a few times + for (int run = 0; run < 3; run++) { + Reopen(&options); + + for (int compact_start = 0; compact_start < N; compact_start += 10) { + for (int i = 0; i < N; i += 10) { + ASSERT_TRUE(Between(Size("", Key(i)), S1*i, S2*i)); + ASSERT_TRUE(Between(Size("", Key(i)+".suffix"), S1*(i+1), S2*(i+1))); + ASSERT_TRUE(Between(Size(Key(i), Key(i+10)), S1*10, S2*10)); + } + ASSERT_TRUE(Between(Size("", Key(50)), S1*50, S2*50)); + ASSERT_TRUE(Between(Size("", Key(50)+".suffix"), S1*50, S2*50)); + + std::string cstart_str = Key(compact_start); + std::string cend_str = Key(compact_start + 9); + Slice cstart = cstart_str; + Slice cend = cend_str; + dbfull()->TEST_CompactRange(0, &cstart, &cend); + } + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GT(NumTableFilesAtLevel(1), 0); + } + } while (ChangeOptions()); +} + +TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) { + do { + Options options = CurrentOptions(); + options.compression = kNoCompression; + Reopen(); + + Random rnd(301); + std::string big1 = RandomString(&rnd, 100000); + ASSERT_OK(Put(Key(0), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(1), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(2), big1)); + ASSERT_OK(Put(Key(3), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(4), big1)); + ASSERT_OK(Put(Key(5), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000))); + ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000))); + + if (options.reuse_logs) { + // Need to force a memtable compaction since recovery does not do so. + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + } + + // Check sizes across recovery by reopening a few times + for (int run = 0; run < 3; run++) { + Reopen(&options); + + ASSERT_TRUE(Between(Size("", Key(0)), 0, 0)); + ASSERT_TRUE(Between(Size("", Key(1)), 10000, 11000)); + ASSERT_TRUE(Between(Size("", Key(2)), 20000, 21000)); + ASSERT_TRUE(Between(Size("", Key(3)), 120000, 121000)); + ASSERT_TRUE(Between(Size("", Key(4)), 130000, 131000)); + ASSERT_TRUE(Between(Size("", Key(5)), 230000, 231000)); + ASSERT_TRUE(Between(Size("", Key(6)), 240000, 241000)); + ASSERT_TRUE(Between(Size("", Key(7)), 540000, 541000)); + ASSERT_TRUE(Between(Size("", Key(8)), 550000, 560000)); + + ASSERT_TRUE(Between(Size(Key(3), Key(5)), 110000, 111000)); + + dbfull()->TEST_CompactRange(0, NULL, NULL); + } + } while (ChangeOptions()); +} + +TEST(DBTest, IteratorPinsRef) { + Put("foo", "hello"); + + // Get iterator that will yield the current contents of the DB. + Iterator* iter = db_->NewIterator(ReadOptions()); + + // Write to force compactions + Put("foo", "newvalue1"); + for (int i = 0; i < 100; i++) { + ASSERT_OK(Put(Key(i), Key(i) + std::string(100000, 'v'))); // 100K values + } + Put("foo", "newvalue2"); + + iter->SeekToFirst(); + ASSERT_TRUE(iter->Valid()); + ASSERT_EQ("foo", iter->key().ToString()); + ASSERT_EQ("hello", iter->value().ToString()); + iter->Next(); + ASSERT_TRUE(!iter->Valid()); + delete iter; +} + +TEST(DBTest, Snapshot) { + do { + Put("foo", "v1"); + const Snapshot* s1 = db_->GetSnapshot(); + Put("foo", "v2"); + const Snapshot* s2 = db_->GetSnapshot(); + Put("foo", "v3"); + const Snapshot* s3 = db_->GetSnapshot(); + + Put("foo", "v4"); + ASSERT_EQ("v1", Get("foo", s1)); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v3", Get("foo", s3)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s3); + ASSERT_EQ("v1", Get("foo", s1)); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s1); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s2); + ASSERT_EQ("v4", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, HiddenValuesAreRemoved) { + do { + Random rnd(301); + FillLevels("a", "z"); + + std::string big = RandomString(&rnd, 50000); + Put("foo", big); + Put("pastfoo", "v"); + const Snapshot* snapshot = db_->GetSnapshot(); + Put("foo", "tiny"); + Put("pastfoo2", "v2"); // Advance sequence number one more + + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + ASSERT_GT(NumTableFilesAtLevel(0), 0); + + ASSERT_EQ(big, Get("foo", snapshot)); + ASSERT_TRUE(Between(Size("", "pastfoo"), 50000, 60000)); + db_->ReleaseSnapshot(snapshot); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny, " + big + " ]"); + Slice x("x"); + dbfull()->TEST_CompactRange(0, NULL, &x); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GE(NumTableFilesAtLevel(1), 1); + dbfull()->TEST_CompactRange(1, NULL, &x); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); + + ASSERT_TRUE(Between(Size("", "pastfoo"), 0, 1000)); + } while (ChangeOptions()); +} + +TEST(DBTest, DeletionMarkers1) { + Put("foo", "v1"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + + // Place a table at level last-1 to prevent merging with preceding mutation + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ(NumTableFilesAtLevel(last), 1); + ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + + Delete("foo"); + Put("foo", "v2"); + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); + Slice z("z"); + dbfull()->TEST_CompactRange(last-2, NULL, &z); + // DEL eliminated, but v1 remains because we aren't compacting that level + // (DEL can be eliminated because v2 hides v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, v1 ]"); + dbfull()->TEST_CompactRange(last-1, NULL, NULL); + // Merging last-1 w/ last, so we are the base level for "foo", so + // DEL is removed. (as is v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ v2 ]"); +} + +TEST(DBTest, DeletionMarkers2) { + Put("foo", "v1"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + + // Place a table at level last-1 to prevent merging with preceding mutation + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ(NumTableFilesAtLevel(last), 1); + ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + + Delete("foo"); + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + dbfull()->TEST_CompactRange(last-2, NULL, NULL); + // DEL kept: "last" file overlaps + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + dbfull()->TEST_CompactRange(last-1, NULL, NULL); + // Merging last-1 w/ last, so we are the base level for "foo", so + // DEL is removed. (as is v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ ]"); +} + +TEST(DBTest, OverlapInLevel0) { + do { + ASSERT_EQ(config::kMaxMemCompactLevel, 2) << "Fix test to match config"; + + // Fill levels 1 and 2 to disable the pushing of new memtables to levels > 0. + ASSERT_OK(Put("100", "v100")); + ASSERT_OK(Put("999", "v999")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Delete("100")); + ASSERT_OK(Delete("999")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("0,1,1", FilesPerLevel()); + + // Make files spanning the following ranges in level-0: + // files[0] 200 .. 900 + // files[1] 300 .. 500 + // Note that files are sorted by smallest key. + ASSERT_OK(Put("300", "v300")); + ASSERT_OK(Put("500", "v500")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Put("200", "v200")); + ASSERT_OK(Put("600", "v600")); + ASSERT_OK(Put("900", "v900")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("2,1,1", FilesPerLevel()); + + // Compact away the placeholder files we created initially + dbfull()->TEST_CompactRange(1, NULL, NULL); + dbfull()->TEST_CompactRange(2, NULL, NULL); + ASSERT_EQ("2", FilesPerLevel()); + + // Do a memtable compaction. Before bug-fix, the compaction would + // not detect the overlap with level-0 files and would incorrectly place + // the deletion in a deeper level. + ASSERT_OK(Delete("600")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("3", FilesPerLevel()); + ASSERT_EQ("NOT_FOUND", Get("600")); + } while (ChangeOptions()); +} + +TEST(DBTest, L0_CompactionBug_Issue44_a) { + Reopen(); + ASSERT_OK(Put("b", "v")); + Reopen(); + ASSERT_OK(Delete("b")); + ASSERT_OK(Delete("a")); + Reopen(); + ASSERT_OK(Delete("a")); + Reopen(); + ASSERT_OK(Put("a", "v")); + Reopen(); + Reopen(); + ASSERT_EQ("(a->v)", Contents()); + DelayMilliseconds(1000); // Wait for compaction to finish + ASSERT_EQ("(a->v)", Contents()); +} + +TEST(DBTest, L0_CompactionBug_Issue44_b) { + Reopen(); + Put("",""); + Reopen(); + Delete("e"); + Put("",""); + Reopen(); + Put("c", "cv"); + Reopen(); + Put("",""); + Reopen(); + Put("",""); + DelayMilliseconds(1000); // Wait for compaction to finish + Reopen(); + Put("d","dv"); + Reopen(); + Put("",""); + Reopen(); + Delete("d"); + Delete("b"); + Reopen(); + ASSERT_EQ("(->)(c->cv)", Contents()); + DelayMilliseconds(1000); // Wait for compaction to finish + ASSERT_EQ("(->)(c->cv)", Contents()); +} + +TEST(DBTest, ComparatorCheck) { + class NewComparator : public Comparator { + public: + virtual const char* Name() const { return "leveldb.NewComparator"; } + virtual int Compare(const Slice& a, const Slice& b) const { + return BytewiseComparator()->Compare(a, b); + } + virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + BytewiseComparator()->FindShortestSeparator(s, l); + } + virtual void FindShortSuccessor(std::string* key) const { + BytewiseComparator()->FindShortSuccessor(key); + } + }; + NewComparator cmp; + Options new_options = CurrentOptions(); + new_options.comparator = &cmp; + Status s = TryReopen(&new_options); + ASSERT_TRUE(!s.ok()); + ASSERT_TRUE(s.ToString().find("comparator") != std::string::npos) + << s.ToString(); +} + +TEST(DBTest, CustomComparator) { + class NumberComparator : public Comparator { + public: + virtual const char* Name() const { return "test.NumberComparator"; } + virtual int Compare(const Slice& a, const Slice& b) const { + return ToNumber(a) - ToNumber(b); + } + virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + ToNumber(*s); // Check format + ToNumber(l); // Check format + } + virtual void FindShortSuccessor(std::string* key) const { + ToNumber(*key); // Check format + } + private: + static int ToNumber(const Slice& x) { + // Check that there are no extra characters. + ASSERT_TRUE(x.size() >= 2 && x[0] == '[' && x[x.size()-1] == ']') + << EscapeString(x); + int val; + char ignored; + ASSERT_TRUE(sscanf(x.ToString().c_str(), "[%i]%c", &val, &ignored) == 1) + << EscapeString(x); + return val; + } + }; + NumberComparator cmp; + Options new_options = CurrentOptions(); + new_options.create_if_missing = true; + new_options.comparator = &cmp; + new_options.filter_policy = NULL; // Cannot use bloom filters + new_options.write_buffer_size = 1000; // Compact more often + DestroyAndReopen(&new_options); + ASSERT_OK(Put("[10]", "ten")); + ASSERT_OK(Put("[0x14]", "twenty")); + for (int i = 0; i < 2; i++) { + ASSERT_EQ("ten", Get("[10]")); + ASSERT_EQ("ten", Get("[0xa]")); + ASSERT_EQ("twenty", Get("[20]")); + ASSERT_EQ("twenty", Get("[0x14]")); + ASSERT_EQ("NOT_FOUND", Get("[15]")); + ASSERT_EQ("NOT_FOUND", Get("[0xf]")); + Compact("[0]", "[9999]"); + } + + for (int run = 0; run < 2; run++) { + for (int i = 0; i < 1000; i++) { + char buf[100]; + snprintf(buf, sizeof(buf), "[%d]", i*10); + ASSERT_OK(Put(buf, buf)); + } + Compact("[0]", "[1000000]"); + } +} + +TEST(DBTest, ManualCompaction) { + ASSERT_EQ(config::kMaxMemCompactLevel, 2) + << "Need to update this test to match kMaxMemCompactLevel"; + + MakeTables(3, "p", "q"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range falls before files + Compact("", "c"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range falls after files + Compact("r", "z"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range overlaps files + Compact("p1", "p9"); + ASSERT_EQ("0,0,1", FilesPerLevel()); + + // Populate a different range + MakeTables(3, "c", "e"); + ASSERT_EQ("1,1,2", FilesPerLevel()); + + // Compact just the new range + Compact("b", "f"); + ASSERT_EQ("0,0,2", FilesPerLevel()); + + // Compact all + MakeTables(1, "a", "z"); + ASSERT_EQ("0,1,2", FilesPerLevel()); + db_->CompactRange(NULL, NULL); + ASSERT_EQ("0,0,1", FilesPerLevel()); +} + +TEST(DBTest, DBOpen_Options) { + std::string dbname = test::TmpDir() + "/db_options_test"; + DestroyDB(dbname, Options()); + + // Does not exist, and create_if_missing == false: error + DB* db = NULL; + Options opts; + opts.create_if_missing = false; + Status s = DB::Open(opts, dbname, &db); + ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != NULL); + ASSERT_TRUE(db == NULL); + + // Does not exist, and create_if_missing == true: OK + opts.create_if_missing = true; + s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; + + // Does exist, and error_if_exists == true: error + opts.create_if_missing = false; + opts.error_if_exists = true; + s = DB::Open(opts, dbname, &db); + ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != NULL); + ASSERT_TRUE(db == NULL); + + // Does exist, and error_if_exists == false: OK + opts.create_if_missing = true; + opts.error_if_exists = false; + s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; +} + +TEST(DBTest, Locking) { + DB* db2 = NULL; + Status s = DB::Open(CurrentOptions(), dbname_, &db2); + ASSERT_TRUE(!s.ok()) << "Locking did not prevent re-opening db"; +} + +// Check that number of files does not grow when we are out of space +TEST(DBTest, NoSpace) { + Options options = CurrentOptions(); + options.env = env_; + Reopen(&options); + + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + Compact("a", "z"); + const int num_files = CountFiles(); + env_->no_space_.Release_Store(env_); // Force out-of-space errors + for (int i = 0; i < 10; i++) { + for (int level = 0; level < config::kNumLevels-1; level++) { + dbfull()->TEST_CompactRange(level, NULL, NULL); + } + } + env_->no_space_.Release_Store(NULL); + ASSERT_LT(CountFiles(), num_files + 3); +} + +TEST(DBTest, NonWritableFileSystem) { + Options options = CurrentOptions(); + options.write_buffer_size = 1000; + options.env = env_; + Reopen(&options); + ASSERT_OK(Put("foo", "v1")); + env_->non_writable_.Release_Store(env_); // Force errors for new files + std::string big(100000, 'x'); + int errors = 0; + for (int i = 0; i < 20; i++) { + fprintf(stderr, "iter %d; errors %d\n", i, errors); + if (!Put("foo", big).ok()) { + errors++; + DelayMilliseconds(100); + } + } + ASSERT_GT(errors, 0); + env_->non_writable_.Release_Store(NULL); +} + +TEST(DBTest, WriteSyncError) { + // Check that log sync errors cause the DB to disallow future writes. + + // (a) Cause log sync calls to fail + Options options = CurrentOptions(); + options.env = env_; + Reopen(&options); + env_->data_sync_error_.Release_Store(env_); + + // (b) Normal write should succeed + WriteOptions w; + ASSERT_OK(db_->Put(w, "k1", "v1")); + ASSERT_EQ("v1", Get("k1")); + + // (c) Do a sync write; should fail + w.sync = true; + ASSERT_TRUE(!db_->Put(w, "k2", "v2").ok()); + ASSERT_EQ("v1", Get("k1")); + ASSERT_EQ("NOT_FOUND", Get("k2")); + + // (d) make sync behave normally + env_->data_sync_error_.Release_Store(NULL); + + // (e) Do a non-sync write; should fail + w.sync = false; + ASSERT_TRUE(!db_->Put(w, "k3", "v3").ok()); + ASSERT_EQ("v1", Get("k1")); + ASSERT_EQ("NOT_FOUND", Get("k2")); + ASSERT_EQ("NOT_FOUND", Get("k3")); +} + +TEST(DBTest, ManifestWriteError) { + // Test for the following problem: + // (a) Compaction produces file F + // (b) Log record containing F is written to MANIFEST file, but Sync() fails + // (c) GC deletes F + // (d) After reopening DB, reads fail since deleted F is named in log record + + // We iterate twice. In the second iteration, everything is the + // same except the log record never makes it to the MANIFEST file. + for (int iter = 0; iter < 2; iter++) { + port::AtomicPointer* error_type = (iter == 0) + ? &env_->manifest_sync_error_ + : &env_->manifest_write_error_; + + // Insert foo=>bar mapping + Options options = CurrentOptions(); + options.env = env_; + options.create_if_missing = true; + options.error_if_exists = false; + DestroyAndReopen(&options); + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ("bar", Get("foo")); + + // Memtable compaction (will succeed) + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("bar", Get("foo")); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo=>bar is now in last level + + // Merging compaction (will fail) + error_type->Release_Store(env_); + dbfull()->TEST_CompactRange(last, NULL, NULL); // Should fail + ASSERT_EQ("bar", Get("foo")); + + // Recovery: should not lose data + error_type->Release_Store(NULL); + Reopen(&options); + ASSERT_EQ("bar", Get("foo")); + } +} + +TEST(DBTest, MissingSSTFile) { + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ("bar", Get("foo")); + + // Dump the memtable to disk. + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("bar", Get("foo")); + + Close(); + ASSERT_TRUE(DeleteAnSSTFile()); + Options options = CurrentOptions(); + options.paranoid_checks = true; + Status s = TryReopen(&options); + ASSERT_TRUE(!s.ok()); + ASSERT_TRUE(s.ToString().find("issing") != std::string::npos) + << s.ToString(); +} + +TEST(DBTest, StillReadSST) { + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ("bar", Get("foo")); + + // Dump the memtable to disk. + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("bar", Get("foo")); + Close(); + ASSERT_GT(RenameLDBToSST(), 0); + Options options = CurrentOptions(); + options.paranoid_checks = true; + Status s = TryReopen(&options); + ASSERT_TRUE(s.ok()); + ASSERT_EQ("bar", Get("foo")); +} + +TEST(DBTest, FilesDeletedAfterCompaction) { + ASSERT_OK(Put("foo", "v2")); + Compact("a", "z"); + const int num_files = CountFiles(); + for (int i = 0; i < 10; i++) { + ASSERT_OK(Put("foo", "v2")); + Compact("a", "z"); + } + ASSERT_EQ(CountFiles(), num_files); +} + +TEST(DBTest, BloomFilter) { + env_->count_random_reads_ = true; + Options options = CurrentOptions(); + options.env = env_; + options.block_cache = NewLRUCache(0); // Prevent cache hits + options.filter_policy = NewBloomFilterPolicy(10); + Reopen(&options); + + // Populate multiple layers + const int N = 10000; + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), Key(i))); + } + Compact("a", "z"); + for (int i = 0; i < N; i += 100) { + ASSERT_OK(Put(Key(i), Key(i))); + } + dbfull()->TEST_CompactMemTable(); + + // Prevent auto compactions triggered by seeks + env_->delay_data_sync_.Release_Store(env_); + + // Lookup present keys. Should rarely read from small sstable. + env_->random_read_counter_.Reset(); + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i), Get(Key(i))); + } + int reads = env_->random_read_counter_.Read(); + fprintf(stderr, "%d present => %d reads\n", N, reads); + ASSERT_GE(reads, N); + ASSERT_LE(reads, N + 2*N/100); + + // Lookup present keys. Should rarely read from either sstable. + env_->random_read_counter_.Reset(); + for (int i = 0; i < N; i++) { + ASSERT_EQ("NOT_FOUND", Get(Key(i) + ".missing")); + } + reads = env_->random_read_counter_.Read(); + fprintf(stderr, "%d missing => %d reads\n", N, reads); + ASSERT_LE(reads, 3*N/100); + + env_->delay_data_sync_.Release_Store(NULL); + Close(); + delete options.block_cache; + delete options.filter_policy; +} + +// Multi-threaded test: +namespace { + +static const int kNumThreads = 4; +static const int kTestSeconds = 10; +static const int kNumKeys = 1000; + +struct MTState { + DBTest* test; + port::AtomicPointer stop; + port::AtomicPointer counter[kNumThreads]; + port::AtomicPointer thread_done[kNumThreads]; +}; + +struct MTThread { + MTState* state; + int id; +}; + +static void MTThreadBody(void* arg) { + MTThread* t = reinterpret_cast(arg); + int id = t->id; + DB* db = t->state->test->db_; + uintptr_t counter = 0; + fprintf(stderr, "... starting thread %d\n", id); + Random rnd(1000 + id); + std::string value; + char valbuf[1500]; + while (t->state->stop.Acquire_Load() == NULL) { + t->state->counter[id].Release_Store(reinterpret_cast(counter)); + + int key = rnd.Uniform(kNumKeys); + char keybuf[20]; + snprintf(keybuf, sizeof(keybuf), "%016d", key); + + if (rnd.OneIn(2)) { + // Write values of the form . + // We add some padding for force compactions. + snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d", + key, id, static_cast(counter)); + ASSERT_OK(db->Put(WriteOptions(), Slice(keybuf), Slice(valbuf))); + } else { + // Read a value and verify that it matches the pattern written above. + Status s = db->Get(ReadOptions(), Slice(keybuf), &value); + if (s.IsNotFound()) { + // Key has not yet been written + } else { + // Check that the writer thread counter is >= the counter in the value + ASSERT_OK(s); + int k, w, c; + ASSERT_EQ(3, sscanf(value.c_str(), "%d.%d.%d", &k, &w, &c)) << value; + ASSERT_EQ(k, key); + ASSERT_GE(w, 0); + ASSERT_LT(w, kNumThreads); + ASSERT_LE(static_cast(c), reinterpret_cast( + t->state->counter[w].Acquire_Load())); + } + } + counter++; + } + t->state->thread_done[id].Release_Store(t); + fprintf(stderr, "... stopping thread %d after %d ops\n", id, int(counter)); +} + +} // namespace + +TEST(DBTest, MultiThreaded) { + do { + // Initialize state + MTState mt; + mt.test = this; + mt.stop.Release_Store(0); + for (int id = 0; id < kNumThreads; id++) { + mt.counter[id].Release_Store(0); + mt.thread_done[id].Release_Store(0); + } + + // Start threads + MTThread thread[kNumThreads]; + for (int id = 0; id < kNumThreads; id++) { + thread[id].state = &mt; + thread[id].id = id; + env_->StartThread(MTThreadBody, &thread[id]); + } + + // Let them run for a while + DelayMilliseconds(kTestSeconds * 1000); + + // Stop the threads and wait for them to finish + mt.stop.Release_Store(&mt); + for (int id = 0; id < kNumThreads; id++) { + while (mt.thread_done[id].Acquire_Load() == NULL) { + DelayMilliseconds(100); + } + } + } while (ChangeOptions()); +} + +namespace { +typedef std::map KVMap; +} + +class ModelDB: public DB { + public: + class ModelSnapshot : public Snapshot { + public: + KVMap map_; + }; + + explicit ModelDB(const Options& options): options_(options) { } + ~ModelDB() { } + virtual Status Put(const WriteOptions& o, const Slice& k, const Slice& v) { + return DB::Put(o, k, v); + } + virtual Status Delete(const WriteOptions& o, const Slice& key) { + return DB::Delete(o, key); + } + virtual Status Get(const ReadOptions& options, + const Slice& key, std::string* value) { + assert(false); // Not implemented + return Status::NotFound(key); + } + virtual Iterator* NewIterator(const ReadOptions& options) { + if (options.snapshot == NULL) { + KVMap* saved = new KVMap; + *saved = map_; + return new ModelIter(saved, true); + } else { + const KVMap* snapshot_state = + &(reinterpret_cast(options.snapshot)->map_); + return new ModelIter(snapshot_state, false); + } + } + virtual const Snapshot* GetSnapshot() { + ModelSnapshot* snapshot = new ModelSnapshot; + snapshot->map_ = map_; + return snapshot; + } + + virtual void ReleaseSnapshot(const Snapshot* snapshot) { + delete reinterpret_cast(snapshot); + } + virtual Status Write(const WriteOptions& options, WriteBatch* batch) { + class Handler : public WriteBatch::Handler { + public: + KVMap* map_; + virtual void Put(const Slice& key, const Slice& value) { + (*map_)[key.ToString()] = value.ToString(); + } + virtual void Delete(const Slice& key) { + map_->erase(key.ToString()); + } + }; + Handler handler; + handler.map_ = &map_; + return batch->Iterate(&handler); + } + + virtual bool GetProperty(const Slice& property, std::string* value) { + return false; + } + virtual void GetApproximateSizes(const Range* r, int n, uint64_t* sizes) { + for (int i = 0; i < n; i++) { + sizes[i] = 0; + } + } + virtual void CompactRange(const Slice* start, const Slice* end) { + } + + private: + class ModelIter: public Iterator { + public: + ModelIter(const KVMap* map, bool owned) + : map_(map), owned_(owned), iter_(map_->end()) { + } + ~ModelIter() { + if (owned_) delete map_; + } + virtual bool Valid() const { return iter_ != map_->end(); } + virtual void SeekToFirst() { iter_ = map_->begin(); } + virtual void SeekToLast() { + if (map_->empty()) { + iter_ = map_->end(); + } else { + iter_ = map_->find(map_->rbegin()->first); + } + } + virtual void Seek(const Slice& k) { + iter_ = map_->lower_bound(k.ToString()); + } + virtual void Next() { ++iter_; } + virtual void Prev() { --iter_; } + virtual Slice key() const { return iter_->first; } + virtual Slice value() const { return iter_->second; } + virtual Status status() const { return Status::OK(); } + private: + const KVMap* const map_; + const bool owned_; // Do we own map_ + KVMap::const_iterator iter_; + }; + const Options options_; + KVMap map_; +}; + +static std::string RandomKey(Random* rnd) { + int len = (rnd->OneIn(3) + ? 1 // Short sometimes to encourage collisions + : (rnd->OneIn(100) ? rnd->Skewed(10) : rnd->Uniform(10))); + return test::RandomKey(rnd, len); +} + +static bool CompareIterators(int step, + DB* model, + DB* db, + const Snapshot* model_snap, + const Snapshot* db_snap) { + ReadOptions options; + options.snapshot = model_snap; + Iterator* miter = model->NewIterator(options); + options.snapshot = db_snap; + Iterator* dbiter = db->NewIterator(options); + bool ok = true; + int count = 0; + for (miter->SeekToFirst(), dbiter->SeekToFirst(); + ok && miter->Valid() && dbiter->Valid(); + miter->Next(), dbiter->Next()) { + count++; + if (miter->key().compare(dbiter->key()) != 0) { + fprintf(stderr, "step %d: Key mismatch: '%s' vs. '%s'\n", + step, + EscapeString(miter->key()).c_str(), + EscapeString(dbiter->key()).c_str()); + ok = false; + break; + } + + if (miter->value().compare(dbiter->value()) != 0) { + fprintf(stderr, "step %d: Value mismatch for key '%s': '%s' vs. '%s'\n", + step, + EscapeString(miter->key()).c_str(), + EscapeString(miter->value()).c_str(), + EscapeString(miter->value()).c_str()); + ok = false; + } + } + + if (ok) { + if (miter->Valid() != dbiter->Valid()) { + fprintf(stderr, "step %d: Mismatch at end of iterators: %d vs. %d\n", + step, miter->Valid(), dbiter->Valid()); + ok = false; + } + } + fprintf(stderr, "%d entries compared: ok=%d\n", count, ok); + delete miter; + delete dbiter; + return ok; +} + +TEST(DBTest, Randomized) { + Random rnd(test::RandomSeed()); + do { + ModelDB model(CurrentOptions()); + const int N = 10000; + const Snapshot* model_snap = NULL; + const Snapshot* db_snap = NULL; + std::string k, v; + for (int step = 0; step < N; step++) { + if (step % 100 == 0) { + fprintf(stderr, "Step %d of %d\n", step, N); + } + // TODO(sanjay): Test Get() works + int p = rnd.Uniform(100); + if (p < 45) { // Put + k = RandomKey(&rnd); + v = RandomString(&rnd, + rnd.OneIn(20) + ? 100 + rnd.Uniform(100) + : rnd.Uniform(8)); + ASSERT_OK(model.Put(WriteOptions(), k, v)); + ASSERT_OK(db_->Put(WriteOptions(), k, v)); + + } else if (p < 90) { // Delete + k = RandomKey(&rnd); + ASSERT_OK(model.Delete(WriteOptions(), k)); + ASSERT_OK(db_->Delete(WriteOptions(), k)); + + + } else { // Multi-element batch + WriteBatch b; + const int num = rnd.Uniform(8); + for (int i = 0; i < num; i++) { + if (i == 0 || !rnd.OneIn(10)) { + k = RandomKey(&rnd); + } else { + // Periodically re-use the same key from the previous iter, so + // we have multiple entries in the write batch for the same key + } + if (rnd.OneIn(2)) { + v = RandomString(&rnd, rnd.Uniform(10)); + b.Put(k, v); + } else { + b.Delete(k); + } + } + ASSERT_OK(model.Write(WriteOptions(), &b)); + ASSERT_OK(db_->Write(WriteOptions(), &b)); + } + + if ((step % 100) == 0) { + ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + ASSERT_TRUE(CompareIterators(step, &model, db_, model_snap, db_snap)); + // Save a snapshot from each DB this time that we'll use next + // time we compare things, to make sure the current state is + // preserved with the snapshot + if (model_snap != NULL) model.ReleaseSnapshot(model_snap); + if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + + Reopen(); + ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + + model_snap = model.GetSnapshot(); + db_snap = db_->GetSnapshot(); + } + } + if (model_snap != NULL) model.ReleaseSnapshot(model_snap); + if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + } while (ChangeOptions()); +} + +std::string MakeKey(unsigned int num) { + char buf[30]; + snprintf(buf, sizeof(buf), "%016u", num); + return std::string(buf); +} + +void BM_LogAndApply(int iters, int num_base_files) { + std::string dbname = test::TmpDir() + "/leveldb_test_benchmark"; + DestroyDB(dbname, Options()); + + DB* db = NULL; + Options opts; + opts.create_if_missing = true; + Status s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; + + Env* env = Env::Default(); + + port::Mutex mu; + MutexLock l(&mu); + + InternalKeyComparator cmp(BytewiseComparator()); + Options options; + VersionSet vset(dbname, &options, NULL, &cmp); + bool save_manifest; + ASSERT_OK(vset.Recover(&save_manifest)); + VersionEdit vbase; + uint64_t fnum = 1; + for (int i = 0; i < num_base_files; i++) { + InternalKey start(MakeKey(2*fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + vbase.AddFile(2, fnum++, 1 /* file size */, start, limit); + } + ASSERT_OK(vset.LogAndApply(&vbase, &mu)); + + uint64_t start_micros = env->NowMicros(); + + for (int i = 0; i < iters; i++) { + VersionEdit vedit; + vedit.DeleteFile(2, fnum); + InternalKey start(MakeKey(2*fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + vedit.AddFile(2, fnum++, 1 /* file size */, start, limit); + vset.LogAndApply(&vedit, &mu); + } + uint64_t stop_micros = env->NowMicros(); + unsigned int us = stop_micros - start_micros; + char buf[16]; + snprintf(buf, sizeof(buf), "%d", num_base_files); + fprintf(stderr, + "BM_LogAndApply/%-6s %8d iters : %9u us (%7.0f us / iter)\n", + buf, iters, us, ((float)us) / iters); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + if (argc > 1 && std::string(argv[1]) == "--benchmark") { + leveldb::BM_LogAndApply(1000, 1); + leveldb::BM_LogAndApply(1000, 100); + leveldb::BM_LogAndApply(1000, 10000); + leveldb::BM_LogAndApply(100, 100000); + return 0; + } + + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dbformat.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dbformat.cc new file mode 100644 index 0000000..20a7ca4 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dbformat.cc @@ -0,0 +1,140 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "db/dbformat.h" +#include "port/port.h" +#include "util/coding.h" + +namespace leveldb { + +static uint64_t PackSequenceAndType(uint64_t seq, ValueType t) { + assert(seq <= kMaxSequenceNumber); + assert(t <= kValueTypeForSeek); + return (seq << 8) | t; +} + +void AppendInternalKey(std::string* result, const ParsedInternalKey& key) { + result->append(key.user_key.data(), key.user_key.size()); + PutFixed64(result, PackSequenceAndType(key.sequence, key.type)); +} + +std::string ParsedInternalKey::DebugString() const { + char buf[50]; + snprintf(buf, sizeof(buf), "' @ %llu : %d", + (unsigned long long) sequence, + int(type)); + std::string result = "'"; + result += EscapeString(user_key.ToString()); + result += buf; + return result; +} + +std::string InternalKey::DebugString() const { + std::string result; + ParsedInternalKey parsed; + if (ParseInternalKey(rep_, &parsed)) { + result = parsed.DebugString(); + } else { + result = "(bad)"; + result.append(EscapeString(rep_)); + } + return result; +} + +const char* InternalKeyComparator::Name() const { + return "leveldb.InternalKeyComparator"; +} + +int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const { + // Order by: + // increasing user key (according to user-supplied comparator) + // decreasing sequence number + // decreasing type (though sequence# should be enough to disambiguate) + int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey)); + if (r == 0) { + const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8); + const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8); + if (anum > bnum) { + r = -1; + } else if (anum < bnum) { + r = +1; + } + } + return r; +} + +void InternalKeyComparator::FindShortestSeparator( + std::string* start, + const Slice& limit) const { + // Attempt to shorten the user portion of the key + Slice user_start = ExtractUserKey(*start); + Slice user_limit = ExtractUserKey(limit); + std::string tmp(user_start.data(), user_start.size()); + user_comparator_->FindShortestSeparator(&tmp, user_limit); + if (tmp.size() < user_start.size() && + user_comparator_->Compare(user_start, tmp) < 0) { + // User key has become shorter physically, but larger logically. + // Tack on the earliest possible number to the shortened user key. + PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + assert(this->Compare(*start, tmp) < 0); + assert(this->Compare(tmp, limit) < 0); + start->swap(tmp); + } +} + +void InternalKeyComparator::FindShortSuccessor(std::string* key) const { + Slice user_key = ExtractUserKey(*key); + std::string tmp(user_key.data(), user_key.size()); + user_comparator_->FindShortSuccessor(&tmp); + if (tmp.size() < user_key.size() && + user_comparator_->Compare(user_key, tmp) < 0) { + // User key has become shorter physically, but larger logically. + // Tack on the earliest possible number to the shortened user key. + PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + assert(this->Compare(*key, tmp) < 0); + key->swap(tmp); + } +} + +const char* InternalFilterPolicy::Name() const { + return user_policy_->Name(); +} + +void InternalFilterPolicy::CreateFilter(const Slice* keys, int n, + std::string* dst) const { + // We rely on the fact that the code in table.cc does not mind us + // adjusting keys[]. + Slice* mkey = const_cast(keys); + for (int i = 0; i < n; i++) { + mkey[i] = ExtractUserKey(keys[i]); + // TODO(sanjay): Suppress dups? + } + user_policy_->CreateFilter(keys, n, dst); +} + +bool InternalFilterPolicy::KeyMayMatch(const Slice& key, const Slice& f) const { + return user_policy_->KeyMayMatch(ExtractUserKey(key), f); +} + +LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) { + size_t usize = user_key.size(); + size_t needed = usize + 13; // A conservative estimate + char* dst; + if (needed <= sizeof(space_)) { + dst = space_; + } else { + dst = new char[needed]; + } + start_ = dst; + dst = EncodeVarint32(dst, usize + 8); + kstart_ = dst; + memcpy(dst, user_key.data(), usize); + dst += usize; + EncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek)); + dst += 8; + end_ = dst; +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dbformat.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dbformat.h new file mode 100644 index 0000000..ea897b1 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dbformat.h @@ -0,0 +1,230 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_DBFORMAT_H_ +#define STORAGE_LEVELDB_DB_DBFORMAT_H_ + +#include +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/filter_policy.h" +#include "leveldb/slice.h" +#include "leveldb/table_builder.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +// Grouping of constants. We may want to make some of these +// parameters set via options. +namespace config { +static const int kNumLevels = 7; + +// Level-0 compaction is started when we hit this many files. +static const int kL0_CompactionTrigger = 4; + +// Soft limit on number of level-0 files. We slow down writes at this point. +static const int kL0_SlowdownWritesTrigger = 8; + +// Maximum number of level-0 files. We stop writes at this point. +static const int kL0_StopWritesTrigger = 12; + +// Maximum level to which a new compacted memtable is pushed if it +// does not create overlap. We try to push to level 2 to avoid the +// relatively expensive level 0=>1 compactions and to avoid some +// expensive manifest file operations. We do not push all the way to +// the largest level since that can generate a lot of wasted disk +// space if the same key space is being repeatedly overwritten. +static const int kMaxMemCompactLevel = 2; + +// Approximate gap in bytes between samples of data read during iteration. +static const int kReadBytesPeriod = 1048576; + +} // namespace config + +class InternalKey; + +// Value types encoded as the last component of internal keys. +// DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk +// data structures. +enum ValueType { + kTypeDeletion = 0x0, + kTypeValue = 0x1 +}; +// kValueTypeForSeek defines the ValueType that should be passed when +// constructing a ParsedInternalKey object for seeking to a particular +// sequence number (since we sort sequence numbers in decreasing order +// and the value type is embedded as the low 8 bits in the sequence +// number in internal keys, we need to use the highest-numbered +// ValueType, not the lowest). +static const ValueType kValueTypeForSeek = kTypeValue; + +typedef uint64_t SequenceNumber; + +// We leave eight bits empty at the bottom so a type and sequence# +// can be packed together into 64-bits. +static const SequenceNumber kMaxSequenceNumber = + ((0x1ull << 56) - 1); + +struct ParsedInternalKey { + Slice user_key; + SequenceNumber sequence; + ValueType type; + + ParsedInternalKey() { } // Intentionally left uninitialized (for speed) + ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t) + : user_key(u), sequence(seq), type(t) { } + std::string DebugString() const; +}; + +// Return the length of the encoding of "key". +inline size_t InternalKeyEncodingLength(const ParsedInternalKey& key) { + return key.user_key.size() + 8; +} + +// Append the serialization of "key" to *result. +extern void AppendInternalKey(std::string* result, + const ParsedInternalKey& key); + +// Attempt to parse an internal key from "internal_key". On success, +// stores the parsed data in "*result", and returns true. +// +// On error, returns false, leaves "*result" in an undefined state. +extern bool ParseInternalKey(const Slice& internal_key, + ParsedInternalKey* result); + +// Returns the user key portion of an internal key. +inline Slice ExtractUserKey(const Slice& internal_key) { + assert(internal_key.size() >= 8); + return Slice(internal_key.data(), internal_key.size() - 8); +} + +inline ValueType ExtractValueType(const Slice& internal_key) { + assert(internal_key.size() >= 8); + const size_t n = internal_key.size(); + uint64_t num = DecodeFixed64(internal_key.data() + n - 8); + unsigned char c = num & 0xff; + return static_cast(c); +} + +// A comparator for internal keys that uses a specified comparator for +// the user key portion and breaks ties by decreasing sequence number. +class InternalKeyComparator : public Comparator { + private: + const Comparator* user_comparator_; + public: + explicit InternalKeyComparator(const Comparator* c) : user_comparator_(c) { } + virtual const char* Name() const; + virtual int Compare(const Slice& a, const Slice& b) const; + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const; + virtual void FindShortSuccessor(std::string* key) const; + + const Comparator* user_comparator() const { return user_comparator_; } + + int Compare(const InternalKey& a, const InternalKey& b) const; +}; + +// Filter policy wrapper that converts from internal keys to user keys +class InternalFilterPolicy : public FilterPolicy { + private: + const FilterPolicy* const user_policy_; + public: + explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) { } + virtual const char* Name() const; + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const; + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const; +}; + +// Modules in this directory should keep internal keys wrapped inside +// the following class instead of plain strings so that we do not +// incorrectly use string comparisons instead of an InternalKeyComparator. +class InternalKey { + private: + std::string rep_; + public: + InternalKey() { } // Leave rep_ as empty to indicate it is invalid + InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) { + AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t)); + } + + void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); } + Slice Encode() const { + assert(!rep_.empty()); + return rep_; + } + + Slice user_key() const { return ExtractUserKey(rep_); } + + void SetFrom(const ParsedInternalKey& p) { + rep_.clear(); + AppendInternalKey(&rep_, p); + } + + void Clear() { rep_.clear(); } + + std::string DebugString() const; +}; + +inline int InternalKeyComparator::Compare( + const InternalKey& a, const InternalKey& b) const { + return Compare(a.Encode(), b.Encode()); +} + +inline bool ParseInternalKey(const Slice& internal_key, + ParsedInternalKey* result) { + const size_t n = internal_key.size(); + if (n < 8) return false; + uint64_t num = DecodeFixed64(internal_key.data() + n - 8); + unsigned char c = num & 0xff; + result->sequence = num >> 8; + result->type = static_cast(c); + result->user_key = Slice(internal_key.data(), n - 8); + return (c <= static_cast(kTypeValue)); +} + +// A helper class useful for DBImpl::Get() +class LookupKey { + public: + // Initialize *this for looking up user_key at a snapshot with + // the specified sequence number. + LookupKey(const Slice& user_key, SequenceNumber sequence); + + ~LookupKey(); + + // Return a key suitable for lookup in a MemTable. + Slice memtable_key() const { return Slice(start_, end_ - start_); } + + // Return an internal key (suitable for passing to an internal iterator) + Slice internal_key() const { return Slice(kstart_, end_ - kstart_); } + + // Return the user key + Slice user_key() const { return Slice(kstart_, end_ - kstart_ - 8); } + + private: + // We construct a char array of the form: + // klength varint32 <-- start_ + // userkey char[klength] <-- kstart_ + // tag uint64 + // <-- end_ + // The array is a suitable MemTable key. + // The suffix starting with "userkey" can be used as an InternalKey. + const char* start_; + const char* kstart_; + const char* end_; + char space_[200]; // Avoid allocation for short keys + + // No copying allowed + LookupKey(const LookupKey&); + void operator=(const LookupKey&); +}; + +inline LookupKey::~LookupKey() { + if (start_ != space_) delete[] start_; +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_DBFORMAT_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dbformat_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dbformat_test.cc new file mode 100644 index 0000000..5d82f5d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dbformat_test.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/dbformat.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +static std::string IKey(const std::string& user_key, + uint64_t seq, + ValueType vt) { + std::string encoded; + AppendInternalKey(&encoded, ParsedInternalKey(user_key, seq, vt)); + return encoded; +} + +static std::string Shorten(const std::string& s, const std::string& l) { + std::string result = s; + InternalKeyComparator(BytewiseComparator()).FindShortestSeparator(&result, l); + return result; +} + +static std::string ShortSuccessor(const std::string& s) { + std::string result = s; + InternalKeyComparator(BytewiseComparator()).FindShortSuccessor(&result); + return result; +} + +static void TestKey(const std::string& key, + uint64_t seq, + ValueType vt) { + std::string encoded = IKey(key, seq, vt); + + Slice in(encoded); + ParsedInternalKey decoded("", 0, kTypeValue); + + ASSERT_TRUE(ParseInternalKey(in, &decoded)); + ASSERT_EQ(key, decoded.user_key.ToString()); + ASSERT_EQ(seq, decoded.sequence); + ASSERT_EQ(vt, decoded.type); + + ASSERT_TRUE(!ParseInternalKey(Slice("bar"), &decoded)); +} + +class FormatTest { }; + +TEST(FormatTest, InternalKey_EncodeDecode) { + const char* keys[] = { "", "k", "hello", "longggggggggggggggggggggg" }; + const uint64_t seq[] = { + 1, 2, 3, + (1ull << 8) - 1, 1ull << 8, (1ull << 8) + 1, + (1ull << 16) - 1, 1ull << 16, (1ull << 16) + 1, + (1ull << 32) - 1, 1ull << 32, (1ull << 32) + 1 + }; + for (int k = 0; k < sizeof(keys) / sizeof(keys[0]); k++) { + for (int s = 0; s < sizeof(seq) / sizeof(seq[0]); s++) { + TestKey(keys[k], seq[s], kTypeValue); + TestKey("hello", 1, kTypeDeletion); + } + } +} + +TEST(FormatTest, InternalKeyShortSeparator) { + // When user keys are same + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 99, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 101, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 100, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 100, kTypeDeletion))); + + // When user keys are misordered + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("bar", 99, kTypeValue))); + + // When user keys are different, but correctly ordered + ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek), + Shorten(IKey("foo", 100, kTypeValue), + IKey("hello", 200, kTypeValue))); + + // When start user key is prefix of limit user key + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foobar", 200, kTypeValue))); + + // When limit user key is prefix of start user key + ASSERT_EQ(IKey("foobar", 100, kTypeValue), + Shorten(IKey("foobar", 100, kTypeValue), + IKey("foo", 200, kTypeValue))); +} + +TEST(FormatTest, InternalKeyShortestSuccessor) { + ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek), + ShortSuccessor(IKey("foo", 100, kTypeValue))); + ASSERT_EQ(IKey("\xff\xff", 100, kTypeValue), + ShortSuccessor(IKey("\xff\xff", 100, kTypeValue))); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dumpfile.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dumpfile.cc new file mode 100644 index 0000000..61c47c2 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dumpfile.cc @@ -0,0 +1,225 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/version_edit.h" +#include "db/write_batch_internal.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "leveldb/options.h" +#include "leveldb/status.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" +#include "util/logging.h" + +namespace leveldb { + +namespace { + +bool GuessType(const std::string& fname, FileType* type) { + size_t pos = fname.rfind('/'); + std::string basename; + if (pos == std::string::npos) { + basename = fname; + } else { + basename = std::string(fname.data() + pos + 1, fname.size() - pos - 1); + } + uint64_t ignored; + return ParseFileName(basename, &ignored, type); +} + +// Notified when log reader encounters corruption. +class CorruptionReporter : public log::Reader::Reporter { + public: + WritableFile* dst_; + virtual void Corruption(size_t bytes, const Status& status) { + std::string r = "corruption: "; + AppendNumberTo(&r, bytes); + r += " bytes; "; + r += status.ToString(); + r.push_back('\n'); + dst_->Append(r); + } +}; + +// Print contents of a log file. (*func)() is called on every record. +Status PrintLogContents(Env* env, const std::string& fname, + void (*func)(uint64_t, Slice, WritableFile*), + WritableFile* dst) { + SequentialFile* file; + Status s = env->NewSequentialFile(fname, &file); + if (!s.ok()) { + return s; + } + CorruptionReporter reporter; + reporter.dst_ = dst; + log::Reader reader(file, &reporter, true, 0); + Slice record; + std::string scratch; + while (reader.ReadRecord(&record, &scratch)) { + (*func)(reader.LastRecordOffset(), record, dst); + } + delete file; + return Status::OK(); +} + +// Called on every item found in a WriteBatch. +class WriteBatchItemPrinter : public WriteBatch::Handler { + public: + WritableFile* dst_; + virtual void Put(const Slice& key, const Slice& value) { + std::string r = " put '"; + AppendEscapedStringTo(&r, key); + r += "' '"; + AppendEscapedStringTo(&r, value); + r += "'\n"; + dst_->Append(r); + } + virtual void Delete(const Slice& key) { + std::string r = " del '"; + AppendEscapedStringTo(&r, key); + r += "'\n"; + dst_->Append(r); + } +}; + + +// Called on every log record (each one of which is a WriteBatch) +// found in a kLogFile. +static void WriteBatchPrinter(uint64_t pos, Slice record, WritableFile* dst) { + std::string r = "--- offset "; + AppendNumberTo(&r, pos); + r += "; "; + if (record.size() < 12) { + r += "log record length "; + AppendNumberTo(&r, record.size()); + r += " is too small\n"; + dst->Append(r); + return; + } + WriteBatch batch; + WriteBatchInternal::SetContents(&batch, record); + r += "sequence "; + AppendNumberTo(&r, WriteBatchInternal::Sequence(&batch)); + r.push_back('\n'); + dst->Append(r); + WriteBatchItemPrinter batch_item_printer; + batch_item_printer.dst_ = dst; + Status s = batch.Iterate(&batch_item_printer); + if (!s.ok()) { + dst->Append(" error: " + s.ToString() + "\n"); + } +} + +Status DumpLog(Env* env, const std::string& fname, WritableFile* dst) { + return PrintLogContents(env, fname, WriteBatchPrinter, dst); +} + +// Called on every log record (each one of which is a WriteBatch) +// found in a kDescriptorFile. +static void VersionEditPrinter(uint64_t pos, Slice record, WritableFile* dst) { + std::string r = "--- offset "; + AppendNumberTo(&r, pos); + r += "; "; + VersionEdit edit; + Status s = edit.DecodeFrom(record); + if (!s.ok()) { + r += s.ToString(); + r.push_back('\n'); + } else { + r += edit.DebugString(); + } + dst->Append(r); +} + +Status DumpDescriptor(Env* env, const std::string& fname, WritableFile* dst) { + return PrintLogContents(env, fname, VersionEditPrinter, dst); +} + +Status DumpTable(Env* env, const std::string& fname, WritableFile* dst) { + uint64_t file_size; + RandomAccessFile* file = NULL; + Table* table = NULL; + Status s = env->GetFileSize(fname, &file_size); + if (s.ok()) { + s = env->NewRandomAccessFile(fname, &file); + } + if (s.ok()) { + // We use the default comparator, which may or may not match the + // comparator used in this database. However this should not cause + // problems since we only use Table operations that do not require + // any comparisons. In particular, we do not call Seek or Prev. + s = Table::Open(Options(), file, file_size, &table); + } + if (!s.ok()) { + delete table; + delete file; + return s; + } + + ReadOptions ro; + ro.fill_cache = false; + Iterator* iter = table->NewIterator(ro); + std::string r; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + r.clear(); + ParsedInternalKey key; + if (!ParseInternalKey(iter->key(), &key)) { + r = "badkey '"; + AppendEscapedStringTo(&r, iter->key()); + r += "' => '"; + AppendEscapedStringTo(&r, iter->value()); + r += "'\n"; + dst->Append(r); + } else { + r = "'"; + AppendEscapedStringTo(&r, key.user_key); + r += "' @ "; + AppendNumberTo(&r, key.sequence); + r += " : "; + if (key.type == kTypeDeletion) { + r += "del"; + } else if (key.type == kTypeValue) { + r += "val"; + } else { + AppendNumberTo(&r, key.type); + } + r += " => '"; + AppendEscapedStringTo(&r, iter->value()); + r += "'\n"; + dst->Append(r); + } + } + s = iter->status(); + if (!s.ok()) { + dst->Append("iterator error: " + s.ToString() + "\n"); + } + + delete iter; + delete table; + delete file; + return Status::OK(); +} + +} // namespace + +Status DumpFile(Env* env, const std::string& fname, WritableFile* dst) { + FileType ftype; + if (!GuessType(fname, &ftype)) { + return Status::InvalidArgument(fname + ": unknown file type"); + } + switch (ftype) { + case kLogFile: return DumpLog(env, fname, dst); + case kDescriptorFile: return DumpDescriptor(env, fname, dst); + case kTableFile: return DumpTable(env, fname, dst); + default: + break; + } + return Status::InvalidArgument(fname + ": not a dump-able file type"); +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/fault_injection_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/fault_injection_test.cc new file mode 100644 index 0000000..875dfe8 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/fault_injection_test.cc @@ -0,0 +1,554 @@ +// Copyright 2014 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// This test uses a custom Env to keep track of the state of a filesystem as of +// the last "sync". It then checks for data loss errors by purposely dropping +// file data (or entire files) not protected by a "sync". + +#include "leveldb/db.h" + +#include +#include +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/log_format.h" +#include "db/version_set.h" +#include "leveldb/cache.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static const int kValueSize = 1000; +static const int kMaxNumValues = 2000; +static const size_t kNumIterations = 3; + +class FaultInjectionTestEnv; + +namespace { + +// Assume a filename, and not a directory name like "/foo/bar/" +static std::string GetDirName(const std::string filename) { + size_t found = filename.find_last_of("/\\"); + if (found == std::string::npos) { + return ""; + } else { + return filename.substr(0, found); + } +} + +Status SyncDir(const std::string& dir) { + // As this is a test it isn't required to *actually* sync this directory. + return Status::OK(); +} + +// A basic file truncation function suitable for this test. +Status Truncate(const std::string& filename, uint64_t length) { + leveldb::Env* env = leveldb::Env::Default(); + + SequentialFile* orig_file; + Status s = env->NewSequentialFile(filename, &orig_file); + if (!s.ok()) + return s; + + char* scratch = new char[length]; + leveldb::Slice result; + s = orig_file->Read(length, &result, scratch); + delete orig_file; + if (s.ok()) { + std::string tmp_name = GetDirName(filename) + "/truncate.tmp"; + WritableFile* tmp_file; + s = env->NewWritableFile(tmp_name, &tmp_file); + if (s.ok()) { + s = tmp_file->Append(result); + delete tmp_file; + if (s.ok()) { + s = env->RenameFile(tmp_name, filename); + } else { + env->DeleteFile(tmp_name); + } + } + } + + delete[] scratch; + + return s; +} + +struct FileState { + std::string filename_; + ssize_t pos_; + ssize_t pos_at_last_sync_; + ssize_t pos_at_last_flush_; + + FileState(const std::string& filename) + : filename_(filename), + pos_(-1), + pos_at_last_sync_(-1), + pos_at_last_flush_(-1) { } + + FileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {} + + bool IsFullySynced() const { return pos_ <= 0 || pos_ == pos_at_last_sync_; } + + Status DropUnsyncedData() const; +}; + +} // anonymous namespace + +// A wrapper around WritableFile which informs another Env whenever this file +// is written to or sync'ed. +class TestWritableFile : public WritableFile { + public: + TestWritableFile(const FileState& state, + WritableFile* f, + FaultInjectionTestEnv* env); + virtual ~TestWritableFile(); + virtual Status Append(const Slice& data); + virtual Status Close(); + virtual Status Flush(); + virtual Status Sync(); + + private: + FileState state_; + WritableFile* target_; + bool writable_file_opened_; + FaultInjectionTestEnv* env_; + + Status SyncParent(); +}; + +class FaultInjectionTestEnv : public EnvWrapper { + public: + FaultInjectionTestEnv() : EnvWrapper(Env::Default()), filesystem_active_(true) {} + virtual ~FaultInjectionTestEnv() { } + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result); + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result); + virtual Status DeleteFile(const std::string& f); + virtual Status RenameFile(const std::string& s, const std::string& t); + + void WritableFileClosed(const FileState& state); + Status DropUnsyncedFileData(); + Status DeleteFilesCreatedAfterLastDirSync(); + void DirWasSynced(); + bool IsFileCreatedSinceLastDirSync(const std::string& filename); + void ResetState(); + void UntrackFile(const std::string& f); + // Setting the filesystem to inactive is the test equivalent to simulating a + // system reset. Setting to inactive will freeze our saved filesystem state so + // that it will stop being recorded. It can then be reset back to the state at + // the time of the reset. + bool IsFilesystemActive() const { return filesystem_active_; } + void SetFilesystemActive(bool active) { filesystem_active_ = active; } + + private: + port::Mutex mutex_; + std::map db_file_state_; + std::set new_files_since_last_dir_sync_; + bool filesystem_active_; // Record flushes, syncs, writes +}; + +TestWritableFile::TestWritableFile(const FileState& state, + WritableFile* f, + FaultInjectionTestEnv* env) + : state_(state), + target_(f), + writable_file_opened_(true), + env_(env) { + assert(f != NULL); +} + +TestWritableFile::~TestWritableFile() { + if (writable_file_opened_) { + Close(); + } + delete target_; +} + +Status TestWritableFile::Append(const Slice& data) { + Status s = target_->Append(data); + if (s.ok() && env_->IsFilesystemActive()) { + state_.pos_ += data.size(); + } + return s; +} + +Status TestWritableFile::Close() { + writable_file_opened_ = false; + Status s = target_->Close(); + if (s.ok()) { + env_->WritableFileClosed(state_); + } + return s; +} + +Status TestWritableFile::Flush() { + Status s = target_->Flush(); + if (s.ok() && env_->IsFilesystemActive()) { + state_.pos_at_last_flush_ = state_.pos_; + } + return s; +} + +Status TestWritableFile::SyncParent() { + Status s = SyncDir(GetDirName(state_.filename_)); + if (s.ok()) { + env_->DirWasSynced(); + } + return s; +} + +Status TestWritableFile::Sync() { + if (!env_->IsFilesystemActive()) { + return Status::OK(); + } + // Ensure new files referred to by the manifest are in the filesystem. + Status s = target_->Sync(); + if (s.ok()) { + state_.pos_at_last_sync_ = state_.pos_; + } + if (env_->IsFileCreatedSinceLastDirSync(state_.filename_)) { + Status ps = SyncParent(); + if (s.ok() && !ps.ok()) { + s = ps; + } + } + return s; +} + +Status FaultInjectionTestEnv::NewWritableFile(const std::string& fname, + WritableFile** result) { + WritableFile* actual_writable_file; + Status s = target()->NewWritableFile(fname, &actual_writable_file); + if (s.ok()) { + FileState state(fname); + state.pos_ = 0; + *result = new TestWritableFile(state, actual_writable_file, this); + // NewWritableFile doesn't append to files, so if the same file is + // opened again then it will be truncated - so forget our saved + // state. + UntrackFile(fname); + MutexLock l(&mutex_); + new_files_since_last_dir_sync_.insert(fname); + } + return s; +} + +Status FaultInjectionTestEnv::NewAppendableFile(const std::string& fname, + WritableFile** result) { + WritableFile* actual_writable_file; + Status s = target()->NewAppendableFile(fname, &actual_writable_file); + if (s.ok()) { + FileState state(fname); + state.pos_ = 0; + { + MutexLock l(&mutex_); + if (db_file_state_.count(fname) == 0) { + new_files_since_last_dir_sync_.insert(fname); + } else { + state = db_file_state_[fname]; + } + } + *result = new TestWritableFile(state, actual_writable_file, this); + } + return s; +} + +Status FaultInjectionTestEnv::DropUnsyncedFileData() { + Status s; + MutexLock l(&mutex_); + for (std::map::const_iterator it = + db_file_state_.begin(); + s.ok() && it != db_file_state_.end(); ++it) { + const FileState& state = it->second; + if (!state.IsFullySynced()) { + s = state.DropUnsyncedData(); + } + } + return s; +} + +void FaultInjectionTestEnv::DirWasSynced() { + MutexLock l(&mutex_); + new_files_since_last_dir_sync_.clear(); +} + +bool FaultInjectionTestEnv::IsFileCreatedSinceLastDirSync( + const std::string& filename) { + MutexLock l(&mutex_); + return new_files_since_last_dir_sync_.find(filename) != + new_files_since_last_dir_sync_.end(); +} + +void FaultInjectionTestEnv::UntrackFile(const std::string& f) { + MutexLock l(&mutex_); + db_file_state_.erase(f); + new_files_since_last_dir_sync_.erase(f); +} + +Status FaultInjectionTestEnv::DeleteFile(const std::string& f) { + Status s = EnvWrapper::DeleteFile(f); + ASSERT_OK(s); + if (s.ok()) { + UntrackFile(f); + } + return s; +} + +Status FaultInjectionTestEnv::RenameFile(const std::string& s, + const std::string& t) { + Status ret = EnvWrapper::RenameFile(s, t); + + if (ret.ok()) { + MutexLock l(&mutex_); + if (db_file_state_.find(s) != db_file_state_.end()) { + db_file_state_[t] = db_file_state_[s]; + db_file_state_.erase(s); + } + + if (new_files_since_last_dir_sync_.erase(s) != 0) { + assert(new_files_since_last_dir_sync_.find(t) == + new_files_since_last_dir_sync_.end()); + new_files_since_last_dir_sync_.insert(t); + } + } + + return ret; +} + +void FaultInjectionTestEnv::ResetState() { + // Since we are not destroying the database, the existing files + // should keep their recorded synced/flushed state. Therefore + // we do not reset db_file_state_ and new_files_since_last_dir_sync_. + MutexLock l(&mutex_); + SetFilesystemActive(true); +} + +Status FaultInjectionTestEnv::DeleteFilesCreatedAfterLastDirSync() { + // Because DeleteFile access this container make a copy to avoid deadlock + mutex_.Lock(); + std::set new_files(new_files_since_last_dir_sync_.begin(), + new_files_since_last_dir_sync_.end()); + mutex_.Unlock(); + Status s; + std::set::const_iterator it; + for (it = new_files.begin(); s.ok() && it != new_files.end(); ++it) { + s = DeleteFile(*it); + } + return s; +} + +void FaultInjectionTestEnv::WritableFileClosed(const FileState& state) { + MutexLock l(&mutex_); + db_file_state_[state.filename_] = state; +} + +Status FileState::DropUnsyncedData() const { + ssize_t sync_pos = pos_at_last_sync_ == -1 ? 0 : pos_at_last_sync_; + return Truncate(filename_, sync_pos); +} + +class FaultInjectionTest { + public: + enum ExpectedVerifResult { VAL_EXPECT_NO_ERROR, VAL_EXPECT_ERROR }; + enum ResetMethod { RESET_DROP_UNSYNCED_DATA, RESET_DELETE_UNSYNCED_FILES }; + + FaultInjectionTestEnv* env_; + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + FaultInjectionTest() + : env_(new FaultInjectionTestEnv), + tiny_cache_(NewLRUCache(100)), + db_(NULL) { + dbname_ = test::TmpDir() + "/fault_test"; + DestroyDB(dbname_, Options()); // Destroy any db from earlier run + options_.reuse_logs = true; + options_.env = env_; + options_.paranoid_checks = true; + options_.block_cache = tiny_cache_; + options_.create_if_missing = true; + } + + ~FaultInjectionTest() { + CloseDB(); + DestroyDB(dbname_, Options()); + delete tiny_cache_; + delete env_; + } + + void ReuseLogs(bool reuse) { + options_.reuse_logs = reuse; + } + + void Build(int start_idx, int num_vals) { + std::string key_space, value_space; + WriteBatch batch; + for (int i = start_idx; i < start_idx + num_vals; i++) { + Slice key = Key(i, &key_space); + batch.Clear(); + batch.Put(key, Value(i, &value_space)); + WriteOptions options; + ASSERT_OK(db_->Write(options, &batch)); + } + } + + Status ReadValue(int i, std::string* val) const { + std::string key_space, value_space; + Slice key = Key(i, &key_space); + Value(i, &value_space); + ReadOptions options; + return db_->Get(options, key, val); + } + + Status Verify(int start_idx, int num_vals, + ExpectedVerifResult expected) const { + std::string val; + std::string value_space; + Status s; + for (int i = start_idx; i < start_idx + num_vals && s.ok(); i++) { + Value(i, &value_space); + s = ReadValue(i, &val); + if (expected == VAL_EXPECT_NO_ERROR) { + if (s.ok()) { + ASSERT_EQ(value_space, val); + } + } else if (s.ok()) { + fprintf(stderr, "Expected an error at %d, but was OK\n", i); + s = Status::IOError(dbname_, "Expected value error:"); + } else { + s = Status::OK(); // An expected error + } + } + return s; + } + + // Return the ith key + Slice Key(int i, std::string* storage) const { + char buf[100]; + snprintf(buf, sizeof(buf), "%016d", i); + storage->assign(buf, strlen(buf)); + return Slice(*storage); + } + + // Return the value to associate with the specified key + Slice Value(int k, std::string* storage) const { + Random r(k); + return test::RandomString(&r, kValueSize, storage); + } + + Status OpenDB() { + delete db_; + db_ = NULL; + env_->ResetState(); + return DB::Open(options_, dbname_, &db_); + } + + void CloseDB() { + delete db_; + db_ = NULL; + } + + void DeleteAllData() { + Iterator* iter = db_->NewIterator(ReadOptions()); + WriteOptions options; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ASSERT_OK(db_->Delete(WriteOptions(), iter->key())); + } + + delete iter; + } + + void ResetDBState(ResetMethod reset_method) { + switch (reset_method) { + case RESET_DROP_UNSYNCED_DATA: + ASSERT_OK(env_->DropUnsyncedFileData()); + break; + case RESET_DELETE_UNSYNCED_FILES: + ASSERT_OK(env_->DeleteFilesCreatedAfterLastDirSync()); + break; + default: + assert(false); + } + } + + void PartialCompactTestPreFault(int num_pre_sync, int num_post_sync) { + DeleteAllData(); + Build(0, num_pre_sync); + db_->CompactRange(NULL, NULL); + Build(num_pre_sync, num_post_sync); + } + + void PartialCompactTestReopenWithFault(ResetMethod reset_method, + int num_pre_sync, + int num_post_sync) { + env_->SetFilesystemActive(false); + CloseDB(); + ResetDBState(reset_method); + ASSERT_OK(OpenDB()); + ASSERT_OK(Verify(0, num_pre_sync, FaultInjectionTest::VAL_EXPECT_NO_ERROR)); + ASSERT_OK(Verify(num_pre_sync, num_post_sync, FaultInjectionTest::VAL_EXPECT_ERROR)); + } + + void NoWriteTestPreFault() { + } + + void NoWriteTestReopenWithFault(ResetMethod reset_method) { + CloseDB(); + ResetDBState(reset_method); + ASSERT_OK(OpenDB()); + } + + void DoTest() { + Random rnd(0); + ASSERT_OK(OpenDB()); + for (size_t idx = 0; idx < kNumIterations; idx++) { + int num_pre_sync = rnd.Uniform(kMaxNumValues); + int num_post_sync = rnd.Uniform(kMaxNumValues); + + PartialCompactTestPreFault(num_pre_sync, num_post_sync); + PartialCompactTestReopenWithFault(RESET_DROP_UNSYNCED_DATA, + num_pre_sync, + num_post_sync); + + NoWriteTestPreFault(); + NoWriteTestReopenWithFault(RESET_DROP_UNSYNCED_DATA); + + PartialCompactTestPreFault(num_pre_sync, num_post_sync); + // No new files created so we expect all values since no files will be + // dropped. + PartialCompactTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES, + num_pre_sync + num_post_sync, + 0); + + NoWriteTestPreFault(); + NoWriteTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES); + } + } +}; + +TEST(FaultInjectionTest, FaultTestNoLogReuse) { + ReuseLogs(false); + DoTest(); +} + +TEST(FaultInjectionTest, FaultTestWithLogReuse) { + ReuseLogs(true); + DoTest(); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/filename.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/filename.cc new file mode 100644 index 0000000..da32946 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/filename.cc @@ -0,0 +1,144 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include "db/filename.h" +#include "db/dbformat.h" +#include "leveldb/env.h" +#include "util/logging.h" + +namespace leveldb { + +// A utility routine: write "data" to the named file and Sync() it. +extern Status WriteStringToFileSync(Env* env, const Slice& data, + const std::string& fname); + +static std::string MakeFileName(const std::string& name, uint64_t number, + const char* suffix) { + char buf[100]; + snprintf(buf, sizeof(buf), "/%06llu.%s", + static_cast(number), + suffix); + return name + buf; +} + +std::string LogFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "log"); +} + +std::string TableFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "ldb"); +} + +std::string SSTTableFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "sst"); +} + +std::string DescriptorFileName(const std::string& dbname, uint64_t number) { + assert(number > 0); + char buf[100]; + snprintf(buf, sizeof(buf), "/MANIFEST-%06llu", + static_cast(number)); + return dbname + buf; +} + +std::string CurrentFileName(const std::string& dbname) { + return dbname + "/CURRENT"; +} + +std::string LockFileName(const std::string& dbname) { + return dbname + "/LOCK"; +} + +std::string TempFileName(const std::string& dbname, uint64_t number) { + assert(number > 0); + return MakeFileName(dbname, number, "dbtmp"); +} + +std::string InfoLogFileName(const std::string& dbname) { + return dbname + "/LOG"; +} + +// Return the name of the old info log file for "dbname". +std::string OldInfoLogFileName(const std::string& dbname) { + return dbname + "/LOG.old"; +} + + +// Owned filenames have the form: +// dbname/CURRENT +// dbname/LOCK +// dbname/LOG +// dbname/LOG.old +// dbname/MANIFEST-[0-9]+ +// dbname/[0-9]+.(log|sst|ldb) +bool ParseFileName(const std::string& fname, + uint64_t* number, + FileType* type) { + Slice rest(fname); + if (rest == "CURRENT") { + *number = 0; + *type = kCurrentFile; + } else if (rest == "LOCK") { + *number = 0; + *type = kDBLockFile; + } else if (rest == "LOG" || rest == "LOG.old") { + *number = 0; + *type = kInfoLogFile; + } else if (rest.starts_with("MANIFEST-")) { + rest.remove_prefix(strlen("MANIFEST-")); + uint64_t num; + if (!ConsumeDecimalNumber(&rest, &num)) { + return false; + } + if (!rest.empty()) { + return false; + } + *type = kDescriptorFile; + *number = num; + } else { + // Avoid strtoull() to keep filename format independent of the + // current locale + uint64_t num; + if (!ConsumeDecimalNumber(&rest, &num)) { + return false; + } + Slice suffix = rest; + if (suffix == Slice(".log")) { + *type = kLogFile; + } else if (suffix == Slice(".sst") || suffix == Slice(".ldb")) { + *type = kTableFile; + } else if (suffix == Slice(".dbtmp")) { + *type = kTempFile; + } else { + return false; + } + *number = num; + } + return true; +} + +Status SetCurrentFile(Env* env, const std::string& dbname, + uint64_t descriptor_number) { + // Remove leading "dbname/" and add newline to manifest file name + std::string manifest = DescriptorFileName(dbname, descriptor_number); + Slice contents = manifest; + assert(contents.starts_with(dbname + "/")); + contents.remove_prefix(dbname.size() + 1); + std::string tmp = TempFileName(dbname, descriptor_number); + Status s = WriteStringToFileSync(env, contents.ToString() + "\n", tmp); + if (s.ok()) { + s = env->RenameFile(tmp, CurrentFileName(dbname)); + } + if (!s.ok()) { + env->DeleteFile(tmp); + } + return s; +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/filename.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/filename.h new file mode 100644 index 0000000..87a7526 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/filename.h @@ -0,0 +1,85 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// File names used by DB code + +#ifndef STORAGE_LEVELDB_DB_FILENAME_H_ +#define STORAGE_LEVELDB_DB_FILENAME_H_ + +#include +#include +#include "leveldb/slice.h" +#include "leveldb/status.h" +#include "port/port.h" + +namespace leveldb { + +class Env; + +enum FileType { + kLogFile, + kDBLockFile, + kTableFile, + kDescriptorFile, + kCurrentFile, + kTempFile, + kInfoLogFile // Either the current one, or an old one +}; + +// Return the name of the log file with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string LogFileName(const std::string& dbname, uint64_t number); + +// Return the name of the sstable with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string TableFileName(const std::string& dbname, uint64_t number); + +// Return the legacy file name for an sstable with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string SSTTableFileName(const std::string& dbname, uint64_t number); + +// Return the name of the descriptor file for the db named by +// "dbname" and the specified incarnation number. The result will be +// prefixed with "dbname". +extern std::string DescriptorFileName(const std::string& dbname, + uint64_t number); + +// Return the name of the current file. This file contains the name +// of the current manifest file. The result will be prefixed with +// "dbname". +extern std::string CurrentFileName(const std::string& dbname); + +// Return the name of the lock file for the db named by +// "dbname". The result will be prefixed with "dbname". +extern std::string LockFileName(const std::string& dbname); + +// Return the name of a temporary file owned by the db named "dbname". +// The result will be prefixed with "dbname". +extern std::string TempFileName(const std::string& dbname, uint64_t number); + +// Return the name of the info log file for "dbname". +extern std::string InfoLogFileName(const std::string& dbname); + +// Return the name of the old info log file for "dbname". +extern std::string OldInfoLogFileName(const std::string& dbname); + +// If filename is a leveldb file, store the type of the file in *type. +// The number encoded in the filename is stored in *number. If the +// filename was successfully parsed, returns true. Else return false. +extern bool ParseFileName(const std::string& filename, + uint64_t* number, + FileType* type); + +// Make the CURRENT file point to the descriptor file with the +// specified number. +extern Status SetCurrentFile(Env* env, const std::string& dbname, + uint64_t descriptor_number); + + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_FILENAME_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/filename_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/filename_test.cc new file mode 100644 index 0000000..a32556d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/filename_test.cc @@ -0,0 +1,123 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/filename.h" + +#include "db/dbformat.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +class FileNameTest { }; + +TEST(FileNameTest, Parse) { + Slice db; + FileType type; + uint64_t number; + + // Successful parses + static struct { + const char* fname; + uint64_t number; + FileType type; + } cases[] = { + { "100.log", 100, kLogFile }, + { "0.log", 0, kLogFile }, + { "0.sst", 0, kTableFile }, + { "0.ldb", 0, kTableFile }, + { "CURRENT", 0, kCurrentFile }, + { "LOCK", 0, kDBLockFile }, + { "MANIFEST-2", 2, kDescriptorFile }, + { "MANIFEST-7", 7, kDescriptorFile }, + { "LOG", 0, kInfoLogFile }, + { "LOG.old", 0, kInfoLogFile }, + { "18446744073709551615.log", 18446744073709551615ull, kLogFile }, + }; + for (int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + std::string f = cases[i].fname; + ASSERT_TRUE(ParseFileName(f, &number, &type)) << f; + ASSERT_EQ(cases[i].type, type) << f; + ASSERT_EQ(cases[i].number, number) << f; + } + + // Errors + static const char* errors[] = { + "", + "foo", + "foo-dx-100.log", + ".log", + "", + "manifest", + "CURREN", + "CURRENTX", + "MANIFES", + "MANIFEST", + "MANIFEST-", + "XMANIFEST-3", + "MANIFEST-3x", + "LOC", + "LOCKx", + "LO", + "LOGx", + "18446744073709551616.log", + "184467440737095516150.log", + "100", + "100.", + "100.lop" + }; + for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) { + std::string f = errors[i]; + ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f; + } +} + +TEST(FileNameTest, Construction) { + uint64_t number; + FileType type; + std::string fname; + + fname = CurrentFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kCurrentFile, type); + + fname = LockFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kDBLockFile, type); + + fname = LogFileName("foo", 192); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(192, number); + ASSERT_EQ(kLogFile, type); + + fname = TableFileName("bar", 200); + ASSERT_EQ("bar/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(200, number); + ASSERT_EQ(kTableFile, type); + + fname = DescriptorFileName("bar", 100); + ASSERT_EQ("bar/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(100, number); + ASSERT_EQ(kDescriptorFile, type); + + fname = TempFileName("tmp", 999); + ASSERT_EQ("tmp/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(999, number); + ASSERT_EQ(kTempFile, type); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/leveldbutil.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/leveldbutil.cc new file mode 100644 index 0000000..9f4b7dd --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/leveldbutil.cc @@ -0,0 +1,64 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "leveldb/dumpfile.h" +#include "leveldb/env.h" +#include "leveldb/status.h" + +namespace leveldb { +namespace { + +class StdoutPrinter : public WritableFile { + public: + virtual Status Append(const Slice& data) { + fwrite(data.data(), 1, data.size(), stdout); + return Status::OK(); + } + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } +}; + +bool HandleDumpCommand(Env* env, char** files, int num) { + StdoutPrinter printer; + bool ok = true; + for (int i = 0; i < num; i++) { + Status s = DumpFile(env, files[i], &printer); + if (!s.ok()) { + fprintf(stderr, "%s\n", s.ToString().c_str()); + ok = false; + } + } + return ok; +} + +} // namespace +} // namespace leveldb + +static void Usage() { + fprintf( + stderr, + "Usage: leveldbutil command...\n" + " dump files... -- dump contents of specified files\n" + ); +} + +int main(int argc, char** argv) { + leveldb::Env* env = leveldb::Env::Default(); + bool ok = true; + if (argc < 2) { + Usage(); + ok = false; + } else { + std::string command = argv[1]; + if (command == "dump") { + ok = leveldb::HandleDumpCommand(env, argv+2, argc-2); + } else { + Usage(); + ok = false; + } + } + return (ok ? 0 : 1); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_format.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_format.h new file mode 100644 index 0000000..356e69f --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_format.h @@ -0,0 +1,35 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Log format information shared by reader and writer. +// See ../doc/log_format.md for more detail. + +#ifndef STORAGE_LEVELDB_DB_LOG_FORMAT_H_ +#define STORAGE_LEVELDB_DB_LOG_FORMAT_H_ + +namespace leveldb { +namespace log { + +enum RecordType { + // Zero is reserved for preallocated files + kZeroType = 0, + + kFullType = 1, + + // For fragments + kFirstType = 2, + kMiddleType = 3, + kLastType = 4 +}; +static const int kMaxRecordType = kLastType; + +static const int kBlockSize = 32768; + +// Header is checksum (4 bytes), length (2 bytes), type (1 byte). +static const int kHeaderSize = 4 + 2 + 1; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_FORMAT_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_reader.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_reader.cc new file mode 100644 index 0000000..a6d3045 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_reader.cc @@ -0,0 +1,284 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_reader.h" + +#include +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { +namespace log { + +Reader::Reporter::~Reporter() { +} + +Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum, + uint64_t initial_offset) + : file_(file), + reporter_(reporter), + checksum_(checksum), + backing_store_(new char[kBlockSize]), + buffer_(), + eof_(false), + last_record_offset_(0), + end_of_buffer_offset_(0), + initial_offset_(initial_offset), + resyncing_(initial_offset > 0) { +} + +Reader::~Reader() { + delete[] backing_store_; +} + +bool Reader::SkipToInitialBlock() { + size_t offset_in_block = initial_offset_ % kBlockSize; + uint64_t block_start_location = initial_offset_ - offset_in_block; + + // Don't search a block if we'd be in the trailer + if (offset_in_block > kBlockSize - 6) { + offset_in_block = 0; + block_start_location += kBlockSize; + } + + end_of_buffer_offset_ = block_start_location; + + // Skip to start of first block that can contain the initial record + if (block_start_location > 0) { + Status skip_status = file_->Skip(block_start_location); + if (!skip_status.ok()) { + ReportDrop(block_start_location, skip_status); + return false; + } + } + + return true; +} + +bool Reader::ReadRecord(Slice* record, std::string* scratch) { + if (last_record_offset_ < initial_offset_) { + if (!SkipToInitialBlock()) { + return false; + } + } + + scratch->clear(); + record->clear(); + bool in_fragmented_record = false; + // Record offset of the logical record that we're reading + // 0 is a dummy value to make compilers happy + uint64_t prospective_record_offset = 0; + + Slice fragment; + while (true) { + const unsigned int record_type = ReadPhysicalRecord(&fragment); + + // ReadPhysicalRecord may have only had an empty trailer remaining in its + // internal buffer. Calculate the offset of the next physical record now + // that it has returned, properly accounting for its header size. + uint64_t physical_record_offset = + end_of_buffer_offset_ - buffer_.size() - kHeaderSize - fragment.size(); + + if (resyncing_) { + if (record_type == kMiddleType) { + continue; + } else if (record_type == kLastType) { + resyncing_ = false; + continue; + } else { + resyncing_ = false; + } + } + + switch (record_type) { + case kFullType: + if (in_fragmented_record) { + // Handle bug in earlier versions of log::Writer where + // it could emit an empty kFirstType record at the tail end + // of a block followed by a kFullType or kFirstType record + // at the beginning of the next block. + if (scratch->empty()) { + in_fragmented_record = false; + } else { + ReportCorruption(scratch->size(), "partial record without end(1)"); + } + } + prospective_record_offset = physical_record_offset; + scratch->clear(); + *record = fragment; + last_record_offset_ = prospective_record_offset; + return true; + + case kFirstType: + if (in_fragmented_record) { + // Handle bug in earlier versions of log::Writer where + // it could emit an empty kFirstType record at the tail end + // of a block followed by a kFullType or kFirstType record + // at the beginning of the next block. + if (scratch->empty()) { + in_fragmented_record = false; + } else { + ReportCorruption(scratch->size(), "partial record without end(2)"); + } + } + prospective_record_offset = physical_record_offset; + scratch->assign(fragment.data(), fragment.size()); + in_fragmented_record = true; + break; + + case kMiddleType: + if (!in_fragmented_record) { + ReportCorruption(fragment.size(), + "missing start of fragmented record(1)"); + } else { + scratch->append(fragment.data(), fragment.size()); + } + break; + + case kLastType: + if (!in_fragmented_record) { + ReportCorruption(fragment.size(), + "missing start of fragmented record(2)"); + } else { + scratch->append(fragment.data(), fragment.size()); + *record = Slice(*scratch); + last_record_offset_ = prospective_record_offset; + return true; + } + break; + + case kEof: + if (in_fragmented_record) { + // This can be caused by the writer dying immediately after + // writing a physical record but before completing the next; don't + // treat it as a corruption, just ignore the entire logical record. + scratch->clear(); + } + return false; + + case kBadRecord: + if (in_fragmented_record) { + ReportCorruption(scratch->size(), "error in middle of record"); + in_fragmented_record = false; + scratch->clear(); + } + break; + + default: { + char buf[40]; + snprintf(buf, sizeof(buf), "unknown record type %u", record_type); + ReportCorruption( + (fragment.size() + (in_fragmented_record ? scratch->size() : 0)), + buf); + in_fragmented_record = false; + scratch->clear(); + break; + } + } + } + return false; +} + +uint64_t Reader::LastRecordOffset() { + return last_record_offset_; +} + +void Reader::ReportCorruption(uint64_t bytes, const char* reason) { + ReportDrop(bytes, Status::Corruption(reason)); +} + +void Reader::ReportDrop(uint64_t bytes, const Status& reason) { + if (reporter_ != NULL && + end_of_buffer_offset_ - buffer_.size() - bytes >= initial_offset_) { + reporter_->Corruption(static_cast(bytes), reason); + } +} + +unsigned int Reader::ReadPhysicalRecord(Slice* result) { + while (true) { + if (buffer_.size() < kHeaderSize) { + if (!eof_) { + // Last read was a full read, so this is a trailer to skip + buffer_.clear(); + Status status = file_->Read(kBlockSize, &buffer_, backing_store_); + end_of_buffer_offset_ += buffer_.size(); + if (!status.ok()) { + buffer_.clear(); + ReportDrop(kBlockSize, status); + eof_ = true; + return kEof; + } else if (buffer_.size() < kBlockSize) { + eof_ = true; + } + continue; + } else { + // Note that if buffer_ is non-empty, we have a truncated header at the + // end of the file, which can be caused by the writer crashing in the + // middle of writing the header. Instead of considering this an error, + // just report EOF. + buffer_.clear(); + return kEof; + } + } + + // Parse the header + const char* header = buffer_.data(); + const uint32_t a = static_cast(header[4]) & 0xff; + const uint32_t b = static_cast(header[5]) & 0xff; + const unsigned int type = header[6]; + const uint32_t length = a | (b << 8); + if (kHeaderSize + length > buffer_.size()) { + size_t drop_size = buffer_.size(); + buffer_.clear(); + if (!eof_) { + ReportCorruption(drop_size, "bad record length"); + return kBadRecord; + } + // If the end of the file has been reached without reading |length| bytes + // of payload, assume the writer died in the middle of writing the record. + // Don't report a corruption. + return kEof; + } + + if (type == kZeroType && length == 0) { + // Skip zero length record without reporting any drops since + // such records are produced by the mmap based writing code in + // env_posix.cc that preallocates file regions. + buffer_.clear(); + return kBadRecord; + } + + // Check crc + if (checksum_) { + uint32_t expected_crc = crc32c::Unmask(DecodeFixed32(header)); + uint32_t actual_crc = crc32c::Value(header + 6, 1 + length); + if (actual_crc != expected_crc) { + // Drop the rest of the buffer since "length" itself may have + // been corrupted and if we trust it, we could find some + // fragment of a real log record that just happens to look + // like a valid log record. + size_t drop_size = buffer_.size(); + buffer_.clear(); + ReportCorruption(drop_size, "checksum mismatch"); + return kBadRecord; + } + } + + buffer_.remove_prefix(kHeaderSize + length); + + // Skip physical record that started before initial_offset_ + if (end_of_buffer_offset_ - buffer_.size() - kHeaderSize - length < + initial_offset_) { + result->clear(); + return kBadRecord; + } + + *result = Slice(header + kHeaderSize, length); + return type; + } +} + +} // namespace log +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_reader.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_reader.h new file mode 100644 index 0000000..8389d61 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_reader.h @@ -0,0 +1,113 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_LOG_READER_H_ +#define STORAGE_LEVELDB_DB_LOG_READER_H_ + +#include + +#include "db/log_format.h" +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class SequentialFile; + +namespace log { + +class Reader { + public: + // Interface for reporting errors. + class Reporter { + public: + virtual ~Reporter(); + + // Some corruption was detected. "size" is the approximate number + // of bytes dropped due to the corruption. + virtual void Corruption(size_t bytes, const Status& status) = 0; + }; + + // Create a reader that will return log records from "*file". + // "*file" must remain live while this Reader is in use. + // + // If "reporter" is non-NULL, it is notified whenever some data is + // dropped due to a detected corruption. "*reporter" must remain + // live while this Reader is in use. + // + // If "checksum" is true, verify checksums if available. + // + // The Reader will start reading at the first record located at physical + // position >= initial_offset within the file. + Reader(SequentialFile* file, Reporter* reporter, bool checksum, + uint64_t initial_offset); + + ~Reader(); + + // Read the next record into *record. Returns true if read + // successfully, false if we hit end of the input. May use + // "*scratch" as temporary storage. The contents filled in *record + // will only be valid until the next mutating operation on this + // reader or the next mutation to *scratch. + bool ReadRecord(Slice* record, std::string* scratch); + + // Returns the physical offset of the last record returned by ReadRecord. + // + // Undefined before the first call to ReadRecord. + uint64_t LastRecordOffset(); + + private: + SequentialFile* const file_; + Reporter* const reporter_; + bool const checksum_; + char* const backing_store_; + Slice buffer_; + bool eof_; // Last Read() indicated EOF by returning < kBlockSize + + // Offset of the last record returned by ReadRecord. + uint64_t last_record_offset_; + // Offset of the first location past the end of buffer_. + uint64_t end_of_buffer_offset_; + + // Offset at which to start looking for the first record to return + uint64_t const initial_offset_; + + // True if we are resynchronizing after a seek (initial_offset_ > 0). In + // particular, a run of kMiddleType and kLastType records can be silently + // skipped in this mode + bool resyncing_; + + // Extend record types with the following special values + enum { + kEof = kMaxRecordType + 1, + // Returned whenever we find an invalid physical record. + // Currently there are three situations in which this happens: + // * The record has an invalid CRC (ReadPhysicalRecord reports a drop) + // * The record is a 0-length record (No drop is reported) + // * The record is below constructor's initial_offset (No drop is reported) + kBadRecord = kMaxRecordType + 2 + }; + + // Skips all blocks that are completely before "initial_offset_". + // + // Returns true on success. Handles reporting. + bool SkipToInitialBlock(); + + // Return type, or one of the preceding special values + unsigned int ReadPhysicalRecord(Slice* result); + + // Reports dropped bytes to the reporter. + // buffer_ must be updated to remove the dropped bytes prior to invocation. + void ReportCorruption(uint64_t bytes, const char* reason); + void ReportDrop(uint64_t bytes, const Status& reason); + + // No copying allowed + Reader(const Reader&); + void operator=(const Reader&); +}; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_READER_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_test.cc new file mode 100644 index 0000000..48a5928 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_test.cc @@ -0,0 +1,591 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { +namespace log { + +// Construct a string of the specified length made out of the supplied +// partial string. +static std::string BigString(const std::string& partial_string, size_t n) { + std::string result; + while (result.size() < n) { + result.append(partial_string); + } + result.resize(n); + return result; +} + +// Construct a string from a number +static std::string NumberString(int n) { + char buf[50]; + snprintf(buf, sizeof(buf), "%d.", n); + return std::string(buf); +} + +// Return a skewed potentially long string +static std::string RandomSkewedString(int i, Random* rnd) { + return BigString(NumberString(i), rnd->Skewed(17)); +} + +class LogTest { + private: + class StringDest : public WritableFile { + public: + std::string contents_; + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + virtual Status Append(const Slice& slice) { + contents_.append(slice.data(), slice.size()); + return Status::OK(); + } + }; + + class StringSource : public SequentialFile { + public: + Slice contents_; + bool force_error_; + bool returned_partial_; + StringSource() : force_error_(false), returned_partial_(false) { } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error"; + + if (force_error_) { + force_error_ = false; + returned_partial_ = true; + return Status::Corruption("read error"); + } + + if (contents_.size() < n) { + n = contents_.size(); + returned_partial_ = true; + } + *result = Slice(contents_.data(), n); + contents_.remove_prefix(n); + return Status::OK(); + } + + virtual Status Skip(uint64_t n) { + if (n > contents_.size()) { + contents_.clear(); + return Status::NotFound("in-memory file skipped past end"); + } + + contents_.remove_prefix(n); + + return Status::OK(); + } + }; + + class ReportCollector : public Reader::Reporter { + public: + size_t dropped_bytes_; + std::string message_; + + ReportCollector() : dropped_bytes_(0) { } + virtual void Corruption(size_t bytes, const Status& status) { + dropped_bytes_ += bytes; + message_.append(status.ToString()); + } + }; + + StringDest dest_; + StringSource source_; + ReportCollector report_; + bool reading_; + Writer* writer_; + Reader* reader_; + + // Record metadata for testing initial offset functionality + static size_t initial_offset_record_sizes_[]; + static uint64_t initial_offset_last_record_offsets_[]; + static int num_initial_offset_records_; + + public: + LogTest() : reading_(false), + writer_(new Writer(&dest_)), + reader_(new Reader(&source_, &report_, true/*checksum*/, + 0/*initial_offset*/)) { + } + + ~LogTest() { + delete writer_; + delete reader_; + } + + void ReopenForAppend() { + delete writer_; + writer_ = new Writer(&dest_, dest_.contents_.size()); + } + + void Write(const std::string& msg) { + ASSERT_TRUE(!reading_) << "Write() after starting to read"; + writer_->AddRecord(Slice(msg)); + } + + size_t WrittenBytes() const { + return dest_.contents_.size(); + } + + std::string Read() { + if (!reading_) { + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + } + std::string scratch; + Slice record; + if (reader_->ReadRecord(&record, &scratch)) { + return record.ToString(); + } else { + return "EOF"; + } + } + + void IncrementByte(int offset, int delta) { + dest_.contents_[offset] += delta; + } + + void SetByte(int offset, char new_byte) { + dest_.contents_[offset] = new_byte; + } + + void ShrinkSize(int bytes) { + dest_.contents_.resize(dest_.contents_.size() - bytes); + } + + void FixChecksum(int header_offset, int len) { + // Compute crc of type/len/data + uint32_t crc = crc32c::Value(&dest_.contents_[header_offset+6], 1 + len); + crc = crc32c::Mask(crc); + EncodeFixed32(&dest_.contents_[header_offset], crc); + } + + void ForceError() { + source_.force_error_ = true; + } + + size_t DroppedBytes() const { + return report_.dropped_bytes_; + } + + std::string ReportMessage() const { + return report_.message_; + } + + // Returns OK iff recorded error message contains "msg" + std::string MatchError(const std::string& msg) const { + if (report_.message_.find(msg) == std::string::npos) { + return report_.message_; + } else { + return "OK"; + } + } + + void WriteInitialOffsetLog() { + for (int i = 0; i < num_initial_offset_records_; i++) { + std::string record(initial_offset_record_sizes_[i], + static_cast('a' + i)); + Write(record); + } + } + + void StartReadingAt(uint64_t initial_offset) { + delete reader_; + reader_ = new Reader(&source_, &report_, true/*checksum*/, initial_offset); + } + + void CheckOffsetPastEndReturnsNoRecords(uint64_t offset_past_end) { + WriteInitialOffsetLog(); + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, + WrittenBytes() + offset_past_end); + Slice record; + std::string scratch; + ASSERT_TRUE(!offset_reader->ReadRecord(&record, &scratch)); + delete offset_reader; + } + + void CheckInitialOffsetRecord(uint64_t initial_offset, + int expected_record_offset) { + WriteInitialOffsetLog(); + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, + initial_offset); + + // Read all records from expected_record_offset through the last one. + ASSERT_LT(expected_record_offset, num_initial_offset_records_); + for (; expected_record_offset < num_initial_offset_records_; + ++expected_record_offset) { + Slice record; + std::string scratch; + ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch)); + ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset], + record.size()); + ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset], + offset_reader->LastRecordOffset()); + ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]); + } + delete offset_reader; + } +}; + +size_t LogTest::initial_offset_record_sizes_[] = + {10000, // Two sizable records in first block + 10000, + 2 * log::kBlockSize - 1000, // Span three blocks + 1, + 13716, // Consume all but two bytes of block 3. + log::kBlockSize - kHeaderSize, // Consume the entirety of block 4. + }; + +uint64_t LogTest::initial_offset_last_record_offsets_[] = + {0, + kHeaderSize + 10000, + 2 * (kHeaderSize + 10000), + 2 * (kHeaderSize + 10000) + + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize, + 2 * (kHeaderSize + 10000) + + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize + + kHeaderSize + 1, + 3 * log::kBlockSize, + }; + +// LogTest::initial_offset_last_record_offsets_ must be defined before this. +int LogTest::num_initial_offset_records_ = + sizeof(LogTest::initial_offset_last_record_offsets_)/sizeof(uint64_t); + +TEST(LogTest, Empty) { + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, ReadWrite) { + Write("foo"); + Write("bar"); + Write(""); + Write("xxxx"); + ASSERT_EQ("foo", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("xxxx", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ("EOF", Read()); // Make sure reads at eof work +} + +TEST(LogTest, ManyBlocks) { + for (int i = 0; i < 100000; i++) { + Write(NumberString(i)); + } + for (int i = 0; i < 100000; i++) { + ASSERT_EQ(NumberString(i), Read()); + } + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, Fragmentation) { + Write("small"); + Write(BigString("medium", 50000)); + Write(BigString("large", 100000)); + ASSERT_EQ("small", Read()); + ASSERT_EQ(BigString("medium", 50000), Read()); + ASSERT_EQ(BigString("large", 100000), Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, MarginalTrailer) { + // Make a trailer that is exactly the same length as an empty record. + const int n = kBlockSize - 2*kHeaderSize; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); + Write(""); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, MarginalTrailer2) { + // Make a trailer that is exactly the same length as an empty record. + const int n = kBlockSize - 2*kHeaderSize; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(0, DroppedBytes()); + ASSERT_EQ("", ReportMessage()); +} + +TEST(LogTest, ShortTrailer) { + const int n = kBlockSize - 2*kHeaderSize + 4; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); + Write(""); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, AlignedEof) { + const int n = kBlockSize - 2*kHeaderSize + 4; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, OpenForAppend) { + Write("hello"); + ReopenForAppend(); + Write("world"); + ASSERT_EQ("hello", Read()); + ASSERT_EQ("world", Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, RandomRead) { + const int N = 500; + Random write_rnd(301); + for (int i = 0; i < N; i++) { + Write(RandomSkewedString(i, &write_rnd)); + } + Random read_rnd(301); + for (int i = 0; i < N; i++) { + ASSERT_EQ(RandomSkewedString(i, &read_rnd), Read()); + } + ASSERT_EQ("EOF", Read()); +} + +// Tests of all the error paths in log_reader.cc follow: + +TEST(LogTest, ReadError) { + Write("foo"); + ForceError(); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(kBlockSize, DroppedBytes()); + ASSERT_EQ("OK", MatchError("read error")); +} + +TEST(LogTest, BadRecordType) { + Write("foo"); + // Type is stored in header[6] + IncrementByte(6, 100); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("unknown record type")); +} + +TEST(LogTest, TruncatedTrailingRecordIsIgnored) { + Write("foo"); + ShrinkSize(4); // Drop all payload as well as a header byte + ASSERT_EQ("EOF", Read()); + // Truncated last record is ignored, not treated as an error. + ASSERT_EQ(0, DroppedBytes()); + ASSERT_EQ("", ReportMessage()); +} + +TEST(LogTest, BadLength) { + const int kPayloadSize = kBlockSize - kHeaderSize; + Write(BigString("bar", kPayloadSize)); + Write("foo"); + // Least significant size byte is stored in header[4]. + IncrementByte(4, 1); + ASSERT_EQ("foo", Read()); + ASSERT_EQ(kBlockSize, DroppedBytes()); + ASSERT_EQ("OK", MatchError("bad record length")); +} + +TEST(LogTest, BadLengthAtEndIsIgnored) { + Write("foo"); + ShrinkSize(1); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(0, DroppedBytes()); + ASSERT_EQ("", ReportMessage()); +} + +TEST(LogTest, ChecksumMismatch) { + Write("foo"); + IncrementByte(0, 10); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(10, DroppedBytes()); + ASSERT_EQ("OK", MatchError("checksum mismatch")); +} + +TEST(LogTest, UnexpectedMiddleType) { + Write("foo"); + SetByte(6, kMiddleType); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("missing start")); +} + +TEST(LogTest, UnexpectedLastType) { + Write("foo"); + SetByte(6, kLastType); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("missing start")); +} + +TEST(LogTest, UnexpectedFullType) { + Write("foo"); + Write("bar"); + SetByte(6, kFirstType); + FixChecksum(0, 3); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("partial record without end")); +} + +TEST(LogTest, UnexpectedFirstType) { + Write("foo"); + Write(BigString("bar", 100000)); + SetByte(6, kFirstType); + FixChecksum(0, 3); + ASSERT_EQ(BigString("bar", 100000), Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("partial record without end")); +} + +TEST(LogTest, MissingLastIsIgnored) { + Write(BigString("bar", kBlockSize)); + // Remove the LAST block, including header. + ShrinkSize(14); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ("", ReportMessage()); + ASSERT_EQ(0, DroppedBytes()); +} + +TEST(LogTest, PartialLastIsIgnored) { + Write(BigString("bar", kBlockSize)); + // Cause a bad record length in the LAST block. + ShrinkSize(1); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ("", ReportMessage()); + ASSERT_EQ(0, DroppedBytes()); +} + +TEST(LogTest, SkipIntoMultiRecord) { + // Consider a fragmented record: + // first(R1), middle(R1), last(R1), first(R2) + // If initial_offset points to a record after first(R1) but before first(R2) + // incomplete fragment errors are not actual errors, and must be suppressed + // until a new first or full record is encountered. + Write(BigString("foo", 3*kBlockSize)); + Write("correct"); + StartReadingAt(kBlockSize); + + ASSERT_EQ("correct", Read()); + ASSERT_EQ("", ReportMessage()); + ASSERT_EQ(0, DroppedBytes()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, ErrorJoinsRecords) { + // Consider two fragmented records: + // first(R1) last(R1) first(R2) last(R2) + // where the middle two fragments disappear. We do not want + // first(R1),last(R2) to get joined and returned as a valid record. + + // Write records that span two blocks + Write(BigString("foo", kBlockSize)); + Write(BigString("bar", kBlockSize)); + Write("correct"); + + // Wipe the middle block + for (int offset = kBlockSize; offset < 2*kBlockSize; offset++) { + SetByte(offset, 'x'); + } + + ASSERT_EQ("correct", Read()); + ASSERT_EQ("EOF", Read()); + const size_t dropped = DroppedBytes(); + ASSERT_LE(dropped, 2*kBlockSize + 100); + ASSERT_GE(dropped, 2*kBlockSize); +} + +TEST(LogTest, ReadStart) { + CheckInitialOffsetRecord(0, 0); +} + +TEST(LogTest, ReadSecondOneOff) { + CheckInitialOffsetRecord(1, 1); +} + +TEST(LogTest, ReadSecondTenThousand) { + CheckInitialOffsetRecord(10000, 1); +} + +TEST(LogTest, ReadSecondStart) { + CheckInitialOffsetRecord(10007, 1); +} + +TEST(LogTest, ReadThirdOneOff) { + CheckInitialOffsetRecord(10008, 2); +} + +TEST(LogTest, ReadThirdStart) { + CheckInitialOffsetRecord(20014, 2); +} + +TEST(LogTest, ReadFourthOneOff) { + CheckInitialOffsetRecord(20015, 3); +} + +TEST(LogTest, ReadFourthFirstBlockTrailer) { + CheckInitialOffsetRecord(log::kBlockSize - 4, 3); +} + +TEST(LogTest, ReadFourthMiddleBlock) { + CheckInitialOffsetRecord(log::kBlockSize + 1, 3); +} + +TEST(LogTest, ReadFourthLastBlock) { + CheckInitialOffsetRecord(2 * log::kBlockSize + 1, 3); +} + +TEST(LogTest, ReadFourthStart) { + CheckInitialOffsetRecord( + 2 * (kHeaderSize + 1000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize, + 3); +} + +TEST(LogTest, ReadInitialOffsetIntoBlockPadding) { + CheckInitialOffsetRecord(3 * log::kBlockSize - 3, 5); +} + +TEST(LogTest, ReadEnd) { + CheckOffsetPastEndReturnsNoRecords(0); +} + +TEST(LogTest, ReadPastEnd) { + CheckOffsetPastEndReturnsNoRecords(5); +} + +} // namespace log +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_writer.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_writer.cc new file mode 100644 index 0000000..74a0327 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_writer.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_writer.h" + +#include +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { +namespace log { + +static void InitTypeCrc(uint32_t* type_crc) { + for (int i = 0; i <= kMaxRecordType; i++) { + char t = static_cast(i); + type_crc[i] = crc32c::Value(&t, 1); + } +} + +Writer::Writer(WritableFile* dest) + : dest_(dest), + block_offset_(0) { + InitTypeCrc(type_crc_); +} + +Writer::Writer(WritableFile* dest, uint64_t dest_length) + : dest_(dest), block_offset_(dest_length % kBlockSize) { + InitTypeCrc(type_crc_); +} + +Writer::~Writer() { +} + +Status Writer::AddRecord(const Slice& slice) { + const char* ptr = slice.data(); + size_t left = slice.size(); + + // Fragment the record if necessary and emit it. Note that if slice + // is empty, we still want to iterate once to emit a single + // zero-length record + Status s; + bool begin = true; + do { + const int leftover = kBlockSize - block_offset_; + assert(leftover >= 0); + if (leftover < kHeaderSize) { + // Switch to a new block + if (leftover > 0) { + // Fill the trailer (literal below relies on kHeaderSize being 7) + assert(kHeaderSize == 7); + dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover)); + } + block_offset_ = 0; + } + + // Invariant: we never leave < kHeaderSize bytes in a block. + assert(kBlockSize - block_offset_ - kHeaderSize >= 0); + + const size_t avail = kBlockSize - block_offset_ - kHeaderSize; + const size_t fragment_length = (left < avail) ? left : avail; + + RecordType type; + const bool end = (left == fragment_length); + if (begin && end) { + type = kFullType; + } else if (begin) { + type = kFirstType; + } else if (end) { + type = kLastType; + } else { + type = kMiddleType; + } + + s = EmitPhysicalRecord(type, ptr, fragment_length); + ptr += fragment_length; + left -= fragment_length; + begin = false; + } while (s.ok() && left > 0); + return s; +} + +Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) { + assert(n <= 0xffff); // Must fit in two bytes + assert(block_offset_ + kHeaderSize + n <= kBlockSize); + + // Format the header + char buf[kHeaderSize]; + buf[4] = static_cast(n & 0xff); + buf[5] = static_cast(n >> 8); + buf[6] = static_cast(t); + + // Compute the crc of the record type and the payload. + uint32_t crc = crc32c::Extend(type_crc_[t], ptr, n); + crc = crc32c::Mask(crc); // Adjust for storage + EncodeFixed32(buf, crc); + + // Write the header and the payload + Status s = dest_->Append(Slice(buf, kHeaderSize)); + if (s.ok()) { + s = dest_->Append(Slice(ptr, n)); + if (s.ok()) { + s = dest_->Flush(); + } + } + block_offset_ += kHeaderSize + n; + return s; +} + +} // namespace log +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_writer.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_writer.h new file mode 100644 index 0000000..9e7cc47 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_writer.h @@ -0,0 +1,54 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_LOG_WRITER_H_ +#define STORAGE_LEVELDB_DB_LOG_WRITER_H_ + +#include +#include "db/log_format.h" +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class WritableFile; + +namespace log { + +class Writer { + public: + // Create a writer that will append data to "*dest". + // "*dest" must be initially empty. + // "*dest" must remain live while this Writer is in use. + explicit Writer(WritableFile* dest); + + // Create a writer that will append data to "*dest". + // "*dest" must have initial length "dest_length". + // "*dest" must remain live while this Writer is in use. + Writer(WritableFile* dest, uint64_t dest_length); + + ~Writer(); + + Status AddRecord(const Slice& slice); + + private: + WritableFile* dest_; + int block_offset_; // Current offset in block + + // crc32c values for all supported record types. These are + // pre-computed to reduce the overhead of computing the crc of the + // record type stored in the header. + uint32_t type_crc_[kMaxRecordType + 1]; + + Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length); + + // No copying allowed + Writer(const Writer&); + void operator=(const Writer&); +}; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_WRITER_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/memtable.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/memtable.cc new file mode 100644 index 0000000..bfec0a7 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/memtable.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/memtable.h" +#include "db/dbformat.h" +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "util/coding.h" + +namespace leveldb { + +static Slice GetLengthPrefixedSlice(const char* data) { + uint32_t len; + const char* p = data; + p = GetVarint32Ptr(p, p + 5, &len); // +5: we assume "p" is not corrupted + return Slice(p, len); +} + +MemTable::MemTable(const InternalKeyComparator& cmp) + : comparator_(cmp), + refs_(0), + table_(comparator_, &arena_) { +} + +MemTable::~MemTable() { + assert(refs_ == 0); +} + +size_t MemTable::ApproximateMemoryUsage() { return arena_.MemoryUsage(); } + +int MemTable::KeyComparator::operator()(const char* aptr, const char* bptr) + const { + // Internal keys are encoded as length-prefixed strings. + Slice a = GetLengthPrefixedSlice(aptr); + Slice b = GetLengthPrefixedSlice(bptr); + return comparator.Compare(a, b); +} + +// Encode a suitable internal key target for "target" and return it. +// Uses *scratch as scratch space, and the returned pointer will point +// into this scratch space. +static const char* EncodeKey(std::string* scratch, const Slice& target) { + scratch->clear(); + PutVarint32(scratch, target.size()); + scratch->append(target.data(), target.size()); + return scratch->data(); +} + +class MemTableIterator: public Iterator { + public: + explicit MemTableIterator(MemTable::Table* table) : iter_(table) { } + + virtual bool Valid() const { return iter_.Valid(); } + virtual void Seek(const Slice& k) { iter_.Seek(EncodeKey(&tmp_, k)); } + virtual void SeekToFirst() { iter_.SeekToFirst(); } + virtual void SeekToLast() { iter_.SeekToLast(); } + virtual void Next() { iter_.Next(); } + virtual void Prev() { iter_.Prev(); } + virtual Slice key() const { return GetLengthPrefixedSlice(iter_.key()); } + virtual Slice value() const { + Slice key_slice = GetLengthPrefixedSlice(iter_.key()); + return GetLengthPrefixedSlice(key_slice.data() + key_slice.size()); + } + + virtual Status status() const { return Status::OK(); } + + private: + MemTable::Table::Iterator iter_; + std::string tmp_; // For passing to EncodeKey + + // No copying allowed + MemTableIterator(const MemTableIterator&); + void operator=(const MemTableIterator&); +}; + +Iterator* MemTable::NewIterator() { + return new MemTableIterator(&table_); +} + +void MemTable::Add(SequenceNumber s, ValueType type, + const Slice& key, + const Slice& value) { + // Format of an entry is concatenation of: + // key_size : varint32 of internal_key.size() + // key bytes : char[internal_key.size()] + // value_size : varint32 of value.size() + // value bytes : char[value.size()] + size_t key_size = key.size(); + size_t val_size = value.size(); + size_t internal_key_size = key_size + 8; + const size_t encoded_len = + VarintLength(internal_key_size) + internal_key_size + + VarintLength(val_size) + val_size; + char* buf = arena_.Allocate(encoded_len); + char* p = EncodeVarint32(buf, internal_key_size); + memcpy(p, key.data(), key_size); + p += key_size; + EncodeFixed64(p, (s << 8) | type); + p += 8; + p = EncodeVarint32(p, val_size); + memcpy(p, value.data(), val_size); + assert((p + val_size) - buf == encoded_len); + table_.Insert(buf); +} + +bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) { + Slice memkey = key.memtable_key(); + Table::Iterator iter(&table_); + iter.Seek(memkey.data()); + if (iter.Valid()) { + // entry format is: + // klength varint32 + // userkey char[klength] + // tag uint64 + // vlength varint32 + // value char[vlength] + // Check that it belongs to same user key. We do not check the + // sequence number since the Seek() call above should have skipped + // all entries with overly large sequence numbers. + const char* entry = iter.key(); + uint32_t key_length; + const char* key_ptr = GetVarint32Ptr(entry, entry+5, &key_length); + if (comparator_.comparator.user_comparator()->Compare( + Slice(key_ptr, key_length - 8), + key.user_key()) == 0) { + // Correct user key + const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8); + switch (static_cast(tag & 0xff)) { + case kTypeValue: { + Slice v = GetLengthPrefixedSlice(key_ptr + key_length); + value->assign(v.data(), v.size()); + return true; + } + case kTypeDeletion: + *s = Status::NotFound(Slice()); + return true; + } + } + } + return false; +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/memtable.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/memtable.h new file mode 100644 index 0000000..9f41567 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/memtable.h @@ -0,0 +1,88 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_MEMTABLE_H_ +#define STORAGE_LEVELDB_DB_MEMTABLE_H_ + +#include +#include "leveldb/db.h" +#include "db/dbformat.h" +#include "db/skiplist.h" +#include "util/arena.h" + +namespace leveldb { + +class InternalKeyComparator; +class Mutex; +class MemTableIterator; + +class MemTable { + public: + // MemTables are reference counted. The initial reference count + // is zero and the caller must call Ref() at least once. + explicit MemTable(const InternalKeyComparator& comparator); + + // Increase reference count. + void Ref() { ++refs_; } + + // Drop reference count. Delete if no more references exist. + void Unref() { + --refs_; + assert(refs_ >= 0); + if (refs_ <= 0) { + delete this; + } + } + + // Returns an estimate of the number of bytes of data in use by this + // data structure. It is safe to call when MemTable is being modified. + size_t ApproximateMemoryUsage(); + + // Return an iterator that yields the contents of the memtable. + // + // The caller must ensure that the underlying MemTable remains live + // while the returned iterator is live. The keys returned by this + // iterator are internal keys encoded by AppendInternalKey in the + // db/format.{h,cc} module. + Iterator* NewIterator(); + + // Add an entry into memtable that maps key to value at the + // specified sequence number and with the specified type. + // Typically value will be empty if type==kTypeDeletion. + void Add(SequenceNumber seq, ValueType type, + const Slice& key, + const Slice& value); + + // If memtable contains a value for key, store it in *value and return true. + // If memtable contains a deletion for key, store a NotFound() error + // in *status and return true. + // Else, return false. + bool Get(const LookupKey& key, std::string* value, Status* s); + + private: + ~MemTable(); // Private since only Unref() should be used to delete it + + struct KeyComparator { + const InternalKeyComparator comparator; + explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { } + int operator()(const char* a, const char* b) const; + }; + friend class MemTableIterator; + friend class MemTableBackwardIterator; + + typedef SkipList Table; + + KeyComparator comparator_; + int refs_; + Arena arena_; + Table table_; + + // No copying allowed + MemTable(const MemTable&); + void operator=(const MemTable&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_MEMTABLE_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/recovery_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/recovery_test.cc new file mode 100644 index 0000000..9596f42 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/recovery_test.cc @@ -0,0 +1,324 @@ +// Copyright (c) 2014 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/write_batch.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class RecoveryTest { + public: + RecoveryTest() : env_(Env::Default()), db_(NULL) { + dbname_ = test::TmpDir() + "/recovery_test"; + DestroyDB(dbname_, Options()); + Open(); + } + + ~RecoveryTest() { + Close(); + DestroyDB(dbname_, Options()); + } + + DBImpl* dbfull() const { return reinterpret_cast(db_); } + Env* env() const { return env_; } + + bool CanAppend() { + WritableFile* tmp; + Status s = env_->NewAppendableFile(CurrentFileName(dbname_), &tmp); + delete tmp; + if (s.IsNotSupportedError()) { + return false; + } else { + return true; + } + } + + void Close() { + delete db_; + db_ = NULL; + } + + void Open(Options* options = NULL) { + Close(); + Options opts; + if (options != NULL) { + opts = *options; + } else { + opts.reuse_logs = true; // TODO(sanjay): test both ways + opts.create_if_missing = true; + } + if (opts.env == NULL) { + opts.env = env_; + } + ASSERT_OK(DB::Open(opts, dbname_, &db_)); + ASSERT_EQ(1, NumLogs()); + } + + Status Put(const std::string& k, const std::string& v) { + return db_->Put(WriteOptions(), k, v); + } + + std::string Get(const std::string& k, const Snapshot* snapshot = NULL) { + std::string result; + Status s = db_->Get(ReadOptions(), k, &result); + if (s.IsNotFound()) { + result = "NOT_FOUND"; + } else if (!s.ok()) { + result = s.ToString(); + } + return result; + } + + std::string ManifestFileName() { + std::string current; + ASSERT_OK(ReadFileToString(env_, CurrentFileName(dbname_), ¤t)); + size_t len = current.size(); + if (len > 0 && current[len-1] == '\n') { + current.resize(len - 1); + } + return dbname_ + "/" + current; + } + + std::string LogName(uint64_t number) { + return LogFileName(dbname_, number); + } + + size_t DeleteLogFiles() { + std::vector logs = GetFiles(kLogFile); + for (size_t i = 0; i < logs.size(); i++) { + ASSERT_OK(env_->DeleteFile(LogName(logs[i]))) << LogName(logs[i]); + } + return logs.size(); + } + + uint64_t FirstLogFile() { + return GetFiles(kLogFile)[0]; + } + + std::vector GetFiles(FileType t) { + std::vector filenames; + ASSERT_OK(env_->GetChildren(dbname_, &filenames)); + std::vector result; + for (size_t i = 0; i < filenames.size(); i++) { + uint64_t number; + FileType type; + if (ParseFileName(filenames[i], &number, &type) && type == t) { + result.push_back(number); + } + } + return result; + } + + int NumLogs() { + return GetFiles(kLogFile).size(); + } + + int NumTables() { + return GetFiles(kTableFile).size(); + } + + uint64_t FileSize(const std::string& fname) { + uint64_t result; + ASSERT_OK(env_->GetFileSize(fname, &result)) << fname; + return result; + } + + void CompactMemTable() { + dbfull()->TEST_CompactMemTable(); + } + + // Directly construct a log file that sets key to val. + void MakeLogFile(uint64_t lognum, SequenceNumber seq, Slice key, Slice val) { + std::string fname = LogFileName(dbname_, lognum); + WritableFile* file; + ASSERT_OK(env_->NewWritableFile(fname, &file)); + log::Writer writer(file); + WriteBatch batch; + batch.Put(key, val); + WriteBatchInternal::SetSequence(&batch, seq); + ASSERT_OK(writer.AddRecord(WriteBatchInternal::Contents(&batch))); + ASSERT_OK(file->Flush()); + delete file; + } + + private: + std::string dbname_; + Env* env_; + DB* db_; +}; + +TEST(RecoveryTest, ManifestReused) { + if (!CanAppend()) { + fprintf(stderr, "skipping test because env does not support appending\n"); + return; + } + ASSERT_OK(Put("foo", "bar")); + Close(); + std::string old_manifest = ManifestFileName(); + Open(); + ASSERT_EQ(old_manifest, ManifestFileName()); + ASSERT_EQ("bar", Get("foo")); + Open(); + ASSERT_EQ(old_manifest, ManifestFileName()); + ASSERT_EQ("bar", Get("foo")); +} + +TEST(RecoveryTest, LargeManifestCompacted) { + if (!CanAppend()) { + fprintf(stderr, "skipping test because env does not support appending\n"); + return; + } + ASSERT_OK(Put("foo", "bar")); + Close(); + std::string old_manifest = ManifestFileName(); + + // Pad with zeroes to make manifest file very big. + { + uint64_t len = FileSize(old_manifest); + WritableFile* file; + ASSERT_OK(env()->NewAppendableFile(old_manifest, &file)); + std::string zeroes(3*1048576 - static_cast(len), 0); + ASSERT_OK(file->Append(zeroes)); + ASSERT_OK(file->Flush()); + delete file; + } + + Open(); + std::string new_manifest = ManifestFileName(); + ASSERT_NE(old_manifest, new_manifest); + ASSERT_GT(10000, FileSize(new_manifest)); + ASSERT_EQ("bar", Get("foo")); + + Open(); + ASSERT_EQ(new_manifest, ManifestFileName()); + ASSERT_EQ("bar", Get("foo")); +} + +TEST(RecoveryTest, NoLogFiles) { + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ(1, DeleteLogFiles()); + Open(); + ASSERT_EQ("NOT_FOUND", Get("foo")); + Open(); + ASSERT_EQ("NOT_FOUND", Get("foo")); +} + +TEST(RecoveryTest, LogFileReuse) { + if (!CanAppend()) { + fprintf(stderr, "skipping test because env does not support appending\n"); + return; + } + for (int i = 0; i < 2; i++) { + ASSERT_OK(Put("foo", "bar")); + if (i == 0) { + // Compact to ensure current log is empty + CompactMemTable(); + } + Close(); + ASSERT_EQ(1, NumLogs()); + uint64_t number = FirstLogFile(); + if (i == 0) { + ASSERT_EQ(0, FileSize(LogName(number))); + } else { + ASSERT_LT(0, FileSize(LogName(number))); + } + Open(); + ASSERT_EQ(1, NumLogs()); + ASSERT_EQ(number, FirstLogFile()) << "did not reuse log file"; + ASSERT_EQ("bar", Get("foo")); + Open(); + ASSERT_EQ(1, NumLogs()); + ASSERT_EQ(number, FirstLogFile()) << "did not reuse log file"; + ASSERT_EQ("bar", Get("foo")); + } +} + +TEST(RecoveryTest, MultipleMemTables) { + // Make a large log. + const int kNum = 1000; + for (int i = 0; i < kNum; i++) { + char buf[100]; + snprintf(buf, sizeof(buf), "%050d", i); + ASSERT_OK(Put(buf, buf)); + } + ASSERT_EQ(0, NumTables()); + Close(); + ASSERT_EQ(0, NumTables()); + ASSERT_EQ(1, NumLogs()); + uint64_t old_log_file = FirstLogFile(); + + // Force creation of multiple memtables by reducing the write buffer size. + Options opt; + opt.reuse_logs = true; + opt.write_buffer_size = (kNum*100) / 2; + Open(&opt); + ASSERT_LE(2, NumTables()); + ASSERT_EQ(1, NumLogs()); + ASSERT_NE(old_log_file, FirstLogFile()) << "must not reuse log"; + for (int i = 0; i < kNum; i++) { + char buf[100]; + snprintf(buf, sizeof(buf), "%050d", i); + ASSERT_EQ(buf, Get(buf)); + } +} + +TEST(RecoveryTest, MultipleLogFiles) { + ASSERT_OK(Put("foo", "bar")); + Close(); + ASSERT_EQ(1, NumLogs()); + + // Make a bunch of uncompacted log files. + uint64_t old_log = FirstLogFile(); + MakeLogFile(old_log+1, 1000, "hello", "world"); + MakeLogFile(old_log+2, 1001, "hi", "there"); + MakeLogFile(old_log+3, 1002, "foo", "bar2"); + + // Recover and check that all log files were processed. + Open(); + ASSERT_LE(1, NumTables()); + ASSERT_EQ(1, NumLogs()); + uint64_t new_log = FirstLogFile(); + ASSERT_LE(old_log+3, new_log); + ASSERT_EQ("bar2", Get("foo")); + ASSERT_EQ("world", Get("hello")); + ASSERT_EQ("there", Get("hi")); + + // Test that previous recovery produced recoverable state. + Open(); + ASSERT_LE(1, NumTables()); + ASSERT_EQ(1, NumLogs()); + if (CanAppend()) { + ASSERT_EQ(new_log, FirstLogFile()); + } + ASSERT_EQ("bar2", Get("foo")); + ASSERT_EQ("world", Get("hello")); + ASSERT_EQ("there", Get("hi")); + + // Check that introducing an older log file does not cause it to be re-read. + Close(); + MakeLogFile(old_log+1, 2000, "hello", "stale write"); + Open(); + ASSERT_LE(1, NumTables()); + ASSERT_EQ(1, NumLogs()); + if (CanAppend()) { + ASSERT_EQ(new_log, FirstLogFile()); + } + ASSERT_EQ("bar2", Get("foo")); + ASSERT_EQ("world", Get("hello")); + ASSERT_EQ("there", Get("hi")); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/repair.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/repair.cc new file mode 100644 index 0000000..4cd4bb0 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/repair.cc @@ -0,0 +1,461 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// We recover the contents of the descriptor from the other files we find. +// (1) Any log files are first converted to tables +// (2) We scan every table to compute +// (a) smallest/largest for the table +// (b) largest sequence number in the table +// (3) We generate descriptor contents: +// - log number is set to zero +// - next-file-number is set to 1 + largest file number we found +// - last-sequence-number is set to largest sequence# found across +// all tables (see 2c) +// - compaction pointers are cleared +// - every table file is added at level 0 +// +// Possible optimization 1: +// (a) Compute total size and use to pick appropriate max-level M +// (b) Sort tables by largest sequence# in the table +// (c) For each table: if it overlaps earlier table, place in level-0, +// else place in level-M. +// Possible optimization 2: +// Store per-table metadata (smallest, largest, largest-seq#, ...) +// in the table's meta section to speed up ScanTable. + +#include "db/builder.h" +#include "db/db_impl.h" +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "db/version_edit.h" +#include "db/write_batch_internal.h" +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/env.h" + +namespace leveldb { + +namespace { + +class Repairer { + public: + Repairer(const std::string& dbname, const Options& options) + : dbname_(dbname), + env_(options.env), + icmp_(options.comparator), + ipolicy_(options.filter_policy), + options_(SanitizeOptions(dbname, &icmp_, &ipolicy_, options)), + owns_info_log_(options_.info_log != options.info_log), + owns_cache_(options_.block_cache != options.block_cache), + next_file_number_(1) { + // TableCache can be small since we expect each table to be opened once. + table_cache_ = new TableCache(dbname_, &options_, 10); + } + + ~Repairer() { + delete table_cache_; + if (owns_info_log_) { + delete options_.info_log; + } + if (owns_cache_) { + delete options_.block_cache; + } + } + + Status Run() { + Status status = FindFiles(); + if (status.ok()) { + ConvertLogFilesToTables(); + ExtractMetaData(); + status = WriteDescriptor(); + } + if (status.ok()) { + unsigned long long bytes = 0; + for (size_t i = 0; i < tables_.size(); i++) { + bytes += tables_[i].meta.file_size; + } + Log(options_.info_log, + "**** Repaired leveldb %s; " + "recovered %d files; %llu bytes. " + "Some data may have been lost. " + "****", + dbname_.c_str(), + static_cast(tables_.size()), + bytes); + } + return status; + } + + private: + struct TableInfo { + FileMetaData meta; + SequenceNumber max_sequence; + }; + + std::string const dbname_; + Env* const env_; + InternalKeyComparator const icmp_; + InternalFilterPolicy const ipolicy_; + Options const options_; + bool owns_info_log_; + bool owns_cache_; + TableCache* table_cache_; + VersionEdit edit_; + + std::vector manifests_; + std::vector table_numbers_; + std::vector logs_; + std::vector tables_; + uint64_t next_file_number_; + + Status FindFiles() { + std::vector filenames; + Status status = env_->GetChildren(dbname_, &filenames); + if (!status.ok()) { + return status; + } + if (filenames.empty()) { + return Status::IOError(dbname_, "repair found no files"); + } + + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + if (type == kDescriptorFile) { + manifests_.push_back(filenames[i]); + } else { + if (number + 1 > next_file_number_) { + next_file_number_ = number + 1; + } + if (type == kLogFile) { + logs_.push_back(number); + } else if (type == kTableFile) { + table_numbers_.push_back(number); + } else { + // Ignore other files + } + } + } + } + return status; + } + + void ConvertLogFilesToTables() { + for (size_t i = 0; i < logs_.size(); i++) { + std::string logname = LogFileName(dbname_, logs_[i]); + Status status = ConvertLogToTable(logs_[i]); + if (!status.ok()) { + Log(options_.info_log, "Log #%llu: ignoring conversion error: %s", + (unsigned long long) logs_[i], + status.ToString().c_str()); + } + ArchiveFile(logname); + } + } + + Status ConvertLogToTable(uint64_t log) { + struct LogReporter : public log::Reader::Reporter { + Env* env; + Logger* info_log; + uint64_t lognum; + virtual void Corruption(size_t bytes, const Status& s) { + // We print error messages for corruption, but continue repairing. + Log(info_log, "Log #%llu: dropping %d bytes; %s", + (unsigned long long) lognum, + static_cast(bytes), + s.ToString().c_str()); + } + }; + + // Open the log file + std::string logname = LogFileName(dbname_, log); + SequentialFile* lfile; + Status status = env_->NewSequentialFile(logname, &lfile); + if (!status.ok()) { + return status; + } + + // Create the log reader. + LogReporter reporter; + reporter.env = env_; + reporter.info_log = options_.info_log; + reporter.lognum = log; + // We intentionally make log::Reader do checksumming so that + // corruptions cause entire commits to be skipped instead of + // propagating bad information (like overly large sequence + // numbers). + log::Reader reader(lfile, &reporter, false/*do not checksum*/, + 0/*initial_offset*/); + + // Read all the records and add to a memtable + std::string scratch; + Slice record; + WriteBatch batch; + MemTable* mem = new MemTable(icmp_); + mem->Ref(); + int counter = 0; + while (reader.ReadRecord(&record, &scratch)) { + if (record.size() < 12) { + reporter.Corruption( + record.size(), Status::Corruption("log record too small")); + continue; + } + WriteBatchInternal::SetContents(&batch, record); + status = WriteBatchInternal::InsertInto(&batch, mem); + if (status.ok()) { + counter += WriteBatchInternal::Count(&batch); + } else { + Log(options_.info_log, "Log #%llu: ignoring %s", + (unsigned long long) log, + status.ToString().c_str()); + status = Status::OK(); // Keep going with rest of file + } + } + delete lfile; + + // Do not record a version edit for this conversion to a Table + // since ExtractMetaData() will also generate edits. + FileMetaData meta; + meta.number = next_file_number_++; + Iterator* iter = mem->NewIterator(); + status = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta); + delete iter; + mem->Unref(); + mem = NULL; + if (status.ok()) { + if (meta.file_size > 0) { + table_numbers_.push_back(meta.number); + } + } + Log(options_.info_log, "Log #%llu: %d ops saved to Table #%llu %s", + (unsigned long long) log, + counter, + (unsigned long long) meta.number, + status.ToString().c_str()); + return status; + } + + void ExtractMetaData() { + for (size_t i = 0; i < table_numbers_.size(); i++) { + ScanTable(table_numbers_[i]); + } + } + + Iterator* NewTableIterator(const FileMetaData& meta) { + // Same as compaction iterators: if paranoid_checks are on, turn + // on checksum verification. + ReadOptions r; + r.verify_checksums = options_.paranoid_checks; + return table_cache_->NewIterator(r, meta.number, meta.file_size); + } + + void ScanTable(uint64_t number) { + TableInfo t; + t.meta.number = number; + std::string fname = TableFileName(dbname_, number); + Status status = env_->GetFileSize(fname, &t.meta.file_size); + if (!status.ok()) { + // Try alternate file name. + fname = SSTTableFileName(dbname_, number); + Status s2 = env_->GetFileSize(fname, &t.meta.file_size); + if (s2.ok()) { + status = Status::OK(); + } + } + if (!status.ok()) { + ArchiveFile(TableFileName(dbname_, number)); + ArchiveFile(SSTTableFileName(dbname_, number)); + Log(options_.info_log, "Table #%llu: dropped: %s", + (unsigned long long) t.meta.number, + status.ToString().c_str()); + return; + } + + // Extract metadata by scanning through table. + int counter = 0; + Iterator* iter = NewTableIterator(t.meta); + bool empty = true; + ParsedInternalKey parsed; + t.max_sequence = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + Slice key = iter->key(); + if (!ParseInternalKey(key, &parsed)) { + Log(options_.info_log, "Table #%llu: unparsable key %s", + (unsigned long long) t.meta.number, + EscapeString(key).c_str()); + continue; + } + + counter++; + if (empty) { + empty = false; + t.meta.smallest.DecodeFrom(key); + } + t.meta.largest.DecodeFrom(key); + if (parsed.sequence > t.max_sequence) { + t.max_sequence = parsed.sequence; + } + } + if (!iter->status().ok()) { + status = iter->status(); + } + delete iter; + Log(options_.info_log, "Table #%llu: %d entries %s", + (unsigned long long) t.meta.number, + counter, + status.ToString().c_str()); + + if (status.ok()) { + tables_.push_back(t); + } else { + RepairTable(fname, t); // RepairTable archives input file. + } + } + + void RepairTable(const std::string& src, TableInfo t) { + // We will copy src contents to a new table and then rename the + // new table over the source. + + // Create builder. + std::string copy = TableFileName(dbname_, next_file_number_++); + WritableFile* file; + Status s = env_->NewWritableFile(copy, &file); + if (!s.ok()) { + return; + } + TableBuilder* builder = new TableBuilder(options_, file); + + // Copy data. + Iterator* iter = NewTableIterator(t.meta); + int counter = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + builder->Add(iter->key(), iter->value()); + counter++; + } + delete iter; + + ArchiveFile(src); + if (counter == 0) { + builder->Abandon(); // Nothing to save + } else { + s = builder->Finish(); + if (s.ok()) { + t.meta.file_size = builder->FileSize(); + } + } + delete builder; + builder = NULL; + + if (s.ok()) { + s = file->Close(); + } + delete file; + file = NULL; + + if (counter > 0 && s.ok()) { + std::string orig = TableFileName(dbname_, t.meta.number); + s = env_->RenameFile(copy, orig); + if (s.ok()) { + Log(options_.info_log, "Table #%llu: %d entries repaired", + (unsigned long long) t.meta.number, counter); + tables_.push_back(t); + } + } + if (!s.ok()) { + env_->DeleteFile(copy); + } + } + + Status WriteDescriptor() { + std::string tmp = TempFileName(dbname_, 1); + WritableFile* file; + Status status = env_->NewWritableFile(tmp, &file); + if (!status.ok()) { + return status; + } + + SequenceNumber max_sequence = 0; + for (size_t i = 0; i < tables_.size(); i++) { + if (max_sequence < tables_[i].max_sequence) { + max_sequence = tables_[i].max_sequence; + } + } + + edit_.SetComparatorName(icmp_.user_comparator()->Name()); + edit_.SetLogNumber(0); + edit_.SetNextFile(next_file_number_); + edit_.SetLastSequence(max_sequence); + + for (size_t i = 0; i < tables_.size(); i++) { + // TODO(opt): separate out into multiple levels + const TableInfo& t = tables_[i]; + edit_.AddFile(0, t.meta.number, t.meta.file_size, + t.meta.smallest, t.meta.largest); + } + + //fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str()); + { + log::Writer log(file); + std::string record; + edit_.EncodeTo(&record); + status = log.AddRecord(record); + } + if (status.ok()) { + status = file->Close(); + } + delete file; + file = NULL; + + if (!status.ok()) { + env_->DeleteFile(tmp); + } else { + // Discard older manifests + for (size_t i = 0; i < manifests_.size(); i++) { + ArchiveFile(dbname_ + "/" + manifests_[i]); + } + + // Install new manifest + status = env_->RenameFile(tmp, DescriptorFileName(dbname_, 1)); + if (status.ok()) { + status = SetCurrentFile(env_, dbname_, 1); + } else { + env_->DeleteFile(tmp); + } + } + return status; + } + + void ArchiveFile(const std::string& fname) { + // Move into another directory. E.g., for + // dir/foo + // rename to + // dir/lost/foo + const char* slash = strrchr(fname.c_str(), '/'); + std::string new_dir; + if (slash != NULL) { + new_dir.assign(fname.data(), slash - fname.data()); + } + new_dir.append("/lost"); + env_->CreateDir(new_dir); // Ignore error + std::string new_file = new_dir; + new_file.append("/"); + new_file.append((slash == NULL) ? fname.c_str() : slash + 1); + Status s = env_->RenameFile(fname, new_file); + Log(options_.info_log, "Archiving %s: %s\n", + fname.c_str(), s.ToString().c_str()); + } +}; +} // namespace + +Status RepairDB(const std::string& dbname, const Options& options) { + Repairer repairer(dbname, options); + return repairer.Run(); +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/skiplist.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/skiplist.h new file mode 100644 index 0000000..8bd7776 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/skiplist.h @@ -0,0 +1,384 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_SKIPLIST_H_ +#define STORAGE_LEVELDB_DB_SKIPLIST_H_ + +// Thread safety +// ------------- +// +// Writes require external synchronization, most likely a mutex. +// Reads require a guarantee that the SkipList will not be destroyed +// while the read is in progress. Apart from that, reads progress +// without any internal locking or synchronization. +// +// Invariants: +// +// (1) Allocated nodes are never deleted until the SkipList is +// destroyed. This is trivially guaranteed by the code since we +// never delete any skip list nodes. +// +// (2) The contents of a Node except for the next/prev pointers are +// immutable after the Node has been linked into the SkipList. +// Only Insert() modifies the list, and it is careful to initialize +// a node and use release-stores to publish the nodes in one or +// more lists. +// +// ... prev vs. next pointer ordering ... + +#include +#include +#include "port/port.h" +#include "util/arena.h" +#include "util/random.h" + +namespace leveldb { + +class Arena; + +template +class SkipList { + private: + struct Node; + + public: + // Create a new SkipList object that will use "cmp" for comparing keys, + // and will allocate memory using "*arena". Objects allocated in the arena + // must remain allocated for the lifetime of the skiplist object. + explicit SkipList(Comparator cmp, Arena* arena); + + // Insert key into the list. + // REQUIRES: nothing that compares equal to key is currently in the list. + void Insert(const Key& key); + + // Returns true iff an entry that compares equal to key is in the list. + bool Contains(const Key& key) const; + + // Iteration over the contents of a skip list + class Iterator { + public: + // Initialize an iterator over the specified list. + // The returned iterator is not valid. + explicit Iterator(const SkipList* list); + + // Returns true iff the iterator is positioned at a valid node. + bool Valid() const; + + // Returns the key at the current position. + // REQUIRES: Valid() + const Key& key() const; + + // Advances to the next position. + // REQUIRES: Valid() + void Next(); + + // Advances to the previous position. + // REQUIRES: Valid() + void Prev(); + + // Advance to the first entry with a key >= target + void Seek(const Key& target); + + // Position at the first entry in list. + // Final state of iterator is Valid() iff list is not empty. + void SeekToFirst(); + + // Position at the last entry in list. + // Final state of iterator is Valid() iff list is not empty. + void SeekToLast(); + + private: + const SkipList* list_; + Node* node_; + // Intentionally copyable + }; + + private: + enum { kMaxHeight = 12 }; + + // Immutable after construction + Comparator const compare_; + Arena* const arena_; // Arena used for allocations of nodes + + Node* const head_; + + // Modified only by Insert(). Read racily by readers, but stale + // values are ok. + port::AtomicPointer max_height_; // Height of the entire list + + inline int GetMaxHeight() const { + return static_cast( + reinterpret_cast(max_height_.NoBarrier_Load())); + } + + // Read/written only by Insert(). + Random rnd_; + + Node* NewNode(const Key& key, int height); + int RandomHeight(); + bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); } + + // Return true if key is greater than the data stored in "n" + bool KeyIsAfterNode(const Key& key, Node* n) const; + + // Return the earliest node that comes at or after key. + // Return NULL if there is no such node. + // + // If prev is non-NULL, fills prev[level] with pointer to previous + // node at "level" for every level in [0..max_height_-1]. + Node* FindGreaterOrEqual(const Key& key, Node** prev) const; + + // Return the latest node with a key < key. + // Return head_ if there is no such node. + Node* FindLessThan(const Key& key) const; + + // Return the last node in the list. + // Return head_ if list is empty. + Node* FindLast() const; + + // No copying allowed + SkipList(const SkipList&); + void operator=(const SkipList&); +}; + +// Implementation details follow +template +struct SkipList::Node { + explicit Node(const Key& k) : key(k) { } + + Key const key; + + // Accessors/mutators for links. Wrapped in methods so we can + // add the appropriate barriers as necessary. + Node* Next(int n) { + assert(n >= 0); + // Use an 'acquire load' so that we observe a fully initialized + // version of the returned Node. + return reinterpret_cast(next_[n].Acquire_Load()); + } + void SetNext(int n, Node* x) { + assert(n >= 0); + // Use a 'release store' so that anybody who reads through this + // pointer observes a fully initialized version of the inserted node. + next_[n].Release_Store(x); + } + + // No-barrier variants that can be safely used in a few locations. + Node* NoBarrier_Next(int n) { + assert(n >= 0); + return reinterpret_cast(next_[n].NoBarrier_Load()); + } + void NoBarrier_SetNext(int n, Node* x) { + assert(n >= 0); + next_[n].NoBarrier_Store(x); + } + + private: + // Array of length equal to the node height. next_[0] is lowest level link. + port::AtomicPointer next_[1]; +}; + +template +typename SkipList::Node* +SkipList::NewNode(const Key& key, int height) { + char* mem = arena_->AllocateAligned( + sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1)); + return new (mem) Node(key); +} + +template +inline SkipList::Iterator::Iterator(const SkipList* list) { + list_ = list; + node_ = NULL; +} + +template +inline bool SkipList::Iterator::Valid() const { + return node_ != NULL; +} + +template +inline const Key& SkipList::Iterator::key() const { + assert(Valid()); + return node_->key; +} + +template +inline void SkipList::Iterator::Next() { + assert(Valid()); + node_ = node_->Next(0); +} + +template +inline void SkipList::Iterator::Prev() { + // Instead of using explicit "prev" links, we just search for the + // last node that falls before key. + assert(Valid()); + node_ = list_->FindLessThan(node_->key); + if (node_ == list_->head_) { + node_ = NULL; + } +} + +template +inline void SkipList::Iterator::Seek(const Key& target) { + node_ = list_->FindGreaterOrEqual(target, NULL); +} + +template +inline void SkipList::Iterator::SeekToFirst() { + node_ = list_->head_->Next(0); +} + +template +inline void SkipList::Iterator::SeekToLast() { + node_ = list_->FindLast(); + if (node_ == list_->head_) { + node_ = NULL; + } +} + +template +int SkipList::RandomHeight() { + // Increase height with probability 1 in kBranching + static const unsigned int kBranching = 4; + int height = 1; + while (height < kMaxHeight && ((rnd_.Next() % kBranching) == 0)) { + height++; + } + assert(height > 0); + assert(height <= kMaxHeight); + return height; +} + +template +bool SkipList::KeyIsAfterNode(const Key& key, Node* n) const { + // NULL n is considered infinite + return (n != NULL) && (compare_(n->key, key) < 0); +} + +template +typename SkipList::Node* SkipList::FindGreaterOrEqual(const Key& key, Node** prev) + const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + Node* next = x->Next(level); + if (KeyIsAfterNode(key, next)) { + // Keep searching in this list + x = next; + } else { + if (prev != NULL) prev[level] = x; + if (level == 0) { + return next; + } else { + // Switch to next list + level--; + } + } + } +} + +template +typename SkipList::Node* +SkipList::FindLessThan(const Key& key) const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + assert(x == head_ || compare_(x->key, key) < 0); + Node* next = x->Next(level); + if (next == NULL || compare_(next->key, key) >= 0) { + if (level == 0) { + return x; + } else { + // Switch to next list + level--; + } + } else { + x = next; + } + } +} + +template +typename SkipList::Node* SkipList::FindLast() + const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + Node* next = x->Next(level); + if (next == NULL) { + if (level == 0) { + return x; + } else { + // Switch to next list + level--; + } + } else { + x = next; + } + } +} + +template +SkipList::SkipList(Comparator cmp, Arena* arena) + : compare_(cmp), + arena_(arena), + head_(NewNode(0 /* any key will do */, kMaxHeight)), + max_height_(reinterpret_cast(1)), + rnd_(0xdeadbeef) { + for (int i = 0; i < kMaxHeight; i++) { + head_->SetNext(i, NULL); + } +} + +template +void SkipList::Insert(const Key& key) { + // TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual() + // here since Insert() is externally synchronized. + Node* prev[kMaxHeight]; + Node* x = FindGreaterOrEqual(key, prev); + + // Our data structure does not allow duplicate insertion + assert(x == NULL || !Equal(key, x->key)); + + int height = RandomHeight(); + if (height > GetMaxHeight()) { + for (int i = GetMaxHeight(); i < height; i++) { + prev[i] = head_; + } + //fprintf(stderr, "Change height from %d to %d\n", max_height_, height); + + // It is ok to mutate max_height_ without any synchronization + // with concurrent readers. A concurrent reader that observes + // the new value of max_height_ will see either the old value of + // new level pointers from head_ (NULL), or a new value set in + // the loop below. In the former case the reader will + // immediately drop to the next level since NULL sorts after all + // keys. In the latter case the reader will use the new node. + max_height_.NoBarrier_Store(reinterpret_cast(height)); + } + + x = NewNode(key, height); + for (int i = 0; i < height; i++) { + // NoBarrier_SetNext() suffices since we will add a barrier when + // we publish a pointer to "x" in prev[i]. + x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i)); + prev[i]->SetNext(i, x); + } +} + +template +bool SkipList::Contains(const Key& key) const { + Node* x = FindGreaterOrEqual(key, NULL); + if (x != NULL && Equal(key, x->key)) { + return true; + } else { + return false; + } +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_SKIPLIST_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/skiplist_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/skiplist_test.cc new file mode 100644 index 0000000..aee1461 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/skiplist_test.cc @@ -0,0 +1,378 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/skiplist.h" +#include +#include "leveldb/env.h" +#include "util/arena.h" +#include "util/hash.h" +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { + +typedef uint64_t Key; + +struct Comparator { + int operator()(const Key& a, const Key& b) const { + if (a < b) { + return -1; + } else if (a > b) { + return +1; + } else { + return 0; + } + } +}; + +class SkipTest { }; + +TEST(SkipTest, Empty) { + Arena arena; + Comparator cmp; + SkipList list(cmp, &arena); + ASSERT_TRUE(!list.Contains(10)); + + SkipList::Iterator iter(&list); + ASSERT_TRUE(!iter.Valid()); + iter.SeekToFirst(); + ASSERT_TRUE(!iter.Valid()); + iter.Seek(100); + ASSERT_TRUE(!iter.Valid()); + iter.SeekToLast(); + ASSERT_TRUE(!iter.Valid()); +} + +TEST(SkipTest, InsertAndLookup) { + const int N = 2000; + const int R = 5000; + Random rnd(1000); + std::set keys; + Arena arena; + Comparator cmp; + SkipList list(cmp, &arena); + for (int i = 0; i < N; i++) { + Key key = rnd.Next() % R; + if (keys.insert(key).second) { + list.Insert(key); + } + } + + for (int i = 0; i < R; i++) { + if (list.Contains(i)) { + ASSERT_EQ(keys.count(i), 1); + } else { + ASSERT_EQ(keys.count(i), 0); + } + } + + // Simple iterator tests + { + SkipList::Iterator iter(&list); + ASSERT_TRUE(!iter.Valid()); + + iter.Seek(0); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.begin()), iter.key()); + + iter.SeekToFirst(); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.begin()), iter.key()); + + iter.SeekToLast(); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.rbegin()), iter.key()); + } + + // Forward iteration test + for (int i = 0; i < R; i++) { + SkipList::Iterator iter(&list); + iter.Seek(i); + + // Compare against model iterator + std::set::iterator model_iter = keys.lower_bound(i); + for (int j = 0; j < 3; j++) { + if (model_iter == keys.end()) { + ASSERT_TRUE(!iter.Valid()); + break; + } else { + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*model_iter, iter.key()); + ++model_iter; + iter.Next(); + } + } + } + + // Backward iteration test + { + SkipList::Iterator iter(&list); + iter.SeekToLast(); + + // Compare against model iterator + for (std::set::reverse_iterator model_iter = keys.rbegin(); + model_iter != keys.rend(); + ++model_iter) { + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*model_iter, iter.key()); + iter.Prev(); + } + ASSERT_TRUE(!iter.Valid()); + } +} + +// We want to make sure that with a single writer and multiple +// concurrent readers (with no synchronization other than when a +// reader's iterator is created), the reader always observes all the +// data that was present in the skip list when the iterator was +// constructor. Because insertions are happening concurrently, we may +// also observe new values that were inserted since the iterator was +// constructed, but we should never miss any values that were present +// at iterator construction time. +// +// We generate multi-part keys: +// +// where: +// key is in range [0..K-1] +// gen is a generation number for key +// hash is hash(key,gen) +// +// The insertion code picks a random key, sets gen to be 1 + the last +// generation number inserted for that key, and sets hash to Hash(key,gen). +// +// At the beginning of a read, we snapshot the last inserted +// generation number for each key. We then iterate, including random +// calls to Next() and Seek(). For every key we encounter, we +// check that it is either expected given the initial snapshot or has +// been concurrently added since the iterator started. +class ConcurrentTest { + private: + static const uint32_t K = 4; + + static uint64_t key(Key key) { return (key >> 40); } + static uint64_t gen(Key key) { return (key >> 8) & 0xffffffffu; } + static uint64_t hash(Key key) { return key & 0xff; } + + static uint64_t HashNumbers(uint64_t k, uint64_t g) { + uint64_t data[2] = { k, g }; + return Hash(reinterpret_cast(data), sizeof(data), 0); + } + + static Key MakeKey(uint64_t k, uint64_t g) { + assert(sizeof(Key) == sizeof(uint64_t)); + assert(k <= K); // We sometimes pass K to seek to the end of the skiplist + assert(g <= 0xffffffffu); + return ((k << 40) | (g << 8) | (HashNumbers(k, g) & 0xff)); + } + + static bool IsValidKey(Key k) { + return hash(k) == (HashNumbers(key(k), gen(k)) & 0xff); + } + + static Key RandomTarget(Random* rnd) { + switch (rnd->Next() % 10) { + case 0: + // Seek to beginning + return MakeKey(0, 0); + case 1: + // Seek to end + return MakeKey(K, 0); + default: + // Seek to middle + return MakeKey(rnd->Next() % K, 0); + } + } + + // Per-key generation + struct State { + port::AtomicPointer generation[K]; + void Set(int k, intptr_t v) { + generation[k].Release_Store(reinterpret_cast(v)); + } + intptr_t Get(int k) { + return reinterpret_cast(generation[k].Acquire_Load()); + } + + State() { + for (int k = 0; k < K; k++) { + Set(k, 0); + } + } + }; + + // Current state of the test + State current_; + + Arena arena_; + + // SkipList is not protected by mu_. We just use a single writer + // thread to modify it. + SkipList list_; + + public: + ConcurrentTest() : list_(Comparator(), &arena_) { } + + // REQUIRES: External synchronization + void WriteStep(Random* rnd) { + const uint32_t k = rnd->Next() % K; + const intptr_t g = current_.Get(k) + 1; + const Key key = MakeKey(k, g); + list_.Insert(key); + current_.Set(k, g); + } + + void ReadStep(Random* rnd) { + // Remember the initial committed state of the skiplist. + State initial_state; + for (int k = 0; k < K; k++) { + initial_state.Set(k, current_.Get(k)); + } + + Key pos = RandomTarget(rnd); + SkipList::Iterator iter(&list_); + iter.Seek(pos); + while (true) { + Key current; + if (!iter.Valid()) { + current = MakeKey(K, 0); + } else { + current = iter.key(); + ASSERT_TRUE(IsValidKey(current)) << current; + } + ASSERT_LE(pos, current) << "should not go backwards"; + + // Verify that everything in [pos,current) was not present in + // initial_state. + while (pos < current) { + ASSERT_LT(key(pos), K) << pos; + + // Note that generation 0 is never inserted, so it is ok if + // <*,0,*> is missing. + ASSERT_TRUE((gen(pos) == 0) || + (gen(pos) > static_cast(initial_state.Get(key(pos)))) + ) << "key: " << key(pos) + << "; gen: " << gen(pos) + << "; initgen: " + << initial_state.Get(key(pos)); + + // Advance to next key in the valid key space + if (key(pos) < key(current)) { + pos = MakeKey(key(pos) + 1, 0); + } else { + pos = MakeKey(key(pos), gen(pos) + 1); + } + } + + if (!iter.Valid()) { + break; + } + + if (rnd->Next() % 2) { + iter.Next(); + pos = MakeKey(key(pos), gen(pos) + 1); + } else { + Key new_target = RandomTarget(rnd); + if (new_target > pos) { + pos = new_target; + iter.Seek(new_target); + } + } + } + } +}; +const uint32_t ConcurrentTest::K; + +// Simple test that does single-threaded testing of the ConcurrentTest +// scaffolding. +TEST(SkipTest, ConcurrentWithoutThreads) { + ConcurrentTest test; + Random rnd(test::RandomSeed()); + for (int i = 0; i < 10000; i++) { + test.ReadStep(&rnd); + test.WriteStep(&rnd); + } +} + +class TestState { + public: + ConcurrentTest t_; + int seed_; + port::AtomicPointer quit_flag_; + + enum ReaderState { + STARTING, + RUNNING, + DONE + }; + + explicit TestState(int s) + : seed_(s), + quit_flag_(NULL), + state_(STARTING), + state_cv_(&mu_) {} + + void Wait(ReaderState s) { + mu_.Lock(); + while (state_ != s) { + state_cv_.Wait(); + } + mu_.Unlock(); + } + + void Change(ReaderState s) { + mu_.Lock(); + state_ = s; + state_cv_.Signal(); + mu_.Unlock(); + } + + private: + port::Mutex mu_; + ReaderState state_; + port::CondVar state_cv_; +}; + +static void ConcurrentReader(void* arg) { + TestState* state = reinterpret_cast(arg); + Random rnd(state->seed_); + int64_t reads = 0; + state->Change(TestState::RUNNING); + while (!state->quit_flag_.Acquire_Load()) { + state->t_.ReadStep(&rnd); + ++reads; + } + state->Change(TestState::DONE); +} + +static void RunConcurrent(int run) { + const int seed = test::RandomSeed() + (run * 100); + Random rnd(seed); + const int N = 1000; + const int kSize = 1000; + for (int i = 0; i < N; i++) { + if ((i % 100) == 0) { + fprintf(stderr, "Run %d of %d\n", i, N); + } + TestState state(seed + 1); + Env::Default()->Schedule(ConcurrentReader, &state); + state.Wait(TestState::RUNNING); + for (int i = 0; i < kSize; i++) { + state.t_.WriteStep(&rnd); + } + state.quit_flag_.Release_Store(&state); // Any non-NULL arg will do + state.Wait(TestState::DONE); + } +} + +TEST(SkipTest, Concurrent1) { RunConcurrent(1); } +TEST(SkipTest, Concurrent2) { RunConcurrent(2); } +TEST(SkipTest, Concurrent3) { RunConcurrent(3); } +TEST(SkipTest, Concurrent4) { RunConcurrent(4); } +TEST(SkipTest, Concurrent5) { RunConcurrent(5); } + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/snapshot.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/snapshot.h new file mode 100644 index 0000000..6ed413c --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/snapshot.h @@ -0,0 +1,67 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_SNAPSHOT_H_ +#define STORAGE_LEVELDB_DB_SNAPSHOT_H_ + +#include "db/dbformat.h" +#include "leveldb/db.h" + +namespace leveldb { + +class SnapshotList; + +// Snapshots are kept in a doubly-linked list in the DB. +// Each SnapshotImpl corresponds to a particular sequence number. +class SnapshotImpl : public Snapshot { + public: + SequenceNumber number_; // const after creation + + private: + friend class SnapshotList; + + // SnapshotImpl is kept in a doubly-linked circular list + SnapshotImpl* prev_; + SnapshotImpl* next_; + + SnapshotList* list_; // just for sanity checks +}; + +class SnapshotList { + public: + SnapshotList() { + list_.prev_ = &list_; + list_.next_ = &list_; + } + + bool empty() const { return list_.next_ == &list_; } + SnapshotImpl* oldest() const { assert(!empty()); return list_.next_; } + SnapshotImpl* newest() const { assert(!empty()); return list_.prev_; } + + const SnapshotImpl* New(SequenceNumber seq) { + SnapshotImpl* s = new SnapshotImpl; + s->number_ = seq; + s->list_ = this; + s->next_ = &list_; + s->prev_ = list_.prev_; + s->prev_->next_ = s; + s->next_->prev_ = s; + return s; + } + + void Delete(const SnapshotImpl* s) { + assert(s->list_ == this); + s->prev_->next_ = s->next_; + s->next_->prev_ = s->prev_; + delete s; + } + + private: + // Dummy head of doubly-linked list of snapshots + SnapshotImpl list_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_SNAPSHOT_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/table_cache.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/table_cache.cc new file mode 100644 index 0000000..e3d82cd --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/table_cache.cc @@ -0,0 +1,127 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/table_cache.h" + +#include "db/filename.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "util/coding.h" + +namespace leveldb { + +struct TableAndFile { + RandomAccessFile* file; + Table* table; +}; + +static void DeleteEntry(const Slice& key, void* value) { + TableAndFile* tf = reinterpret_cast(value); + delete tf->table; + delete tf->file; + delete tf; +} + +static void UnrefEntry(void* arg1, void* arg2) { + Cache* cache = reinterpret_cast(arg1); + Cache::Handle* h = reinterpret_cast(arg2); + cache->Release(h); +} + +TableCache::TableCache(const std::string& dbname, + const Options* options, + int entries) + : env_(options->env), + dbname_(dbname), + options_(options), + cache_(NewLRUCache(entries)) { +} + +TableCache::~TableCache() { + delete cache_; +} + +Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, + Cache::Handle** handle) { + Status s; + char buf[sizeof(file_number)]; + EncodeFixed64(buf, file_number); + Slice key(buf, sizeof(buf)); + *handle = cache_->Lookup(key); + if (*handle == NULL) { + std::string fname = TableFileName(dbname_, file_number); + RandomAccessFile* file = NULL; + Table* table = NULL; + s = env_->NewRandomAccessFile(fname, &file); + if (!s.ok()) { + std::string old_fname = SSTTableFileName(dbname_, file_number); + if (env_->NewRandomAccessFile(old_fname, &file).ok()) { + s = Status::OK(); + } + } + if (s.ok()) { + s = Table::Open(*options_, file, file_size, &table); + } + + if (!s.ok()) { + assert(table == NULL); + delete file; + // We do not cache error results so that if the error is transient, + // or somebody repairs the file, we recover automatically. + } else { + TableAndFile* tf = new TableAndFile; + tf->file = file; + tf->table = table; + *handle = cache_->Insert(key, tf, 1, &DeleteEntry); + } + } + return s; +} + +Iterator* TableCache::NewIterator(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + Table** tableptr) { + if (tableptr != NULL) { + *tableptr = NULL; + } + + Cache::Handle* handle = NULL; + Status s = FindTable(file_number, file_size, &handle); + if (!s.ok()) { + return NewErrorIterator(s); + } + + Table* table = reinterpret_cast(cache_->Value(handle))->table; + Iterator* result = table->NewIterator(options); + result->RegisterCleanup(&UnrefEntry, cache_, handle); + if (tableptr != NULL) { + *tableptr = table; + } + return result; +} + +Status TableCache::Get(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + const Slice& k, + void* arg, + void (*saver)(void*, const Slice&, const Slice&)) { + Cache::Handle* handle = NULL; + Status s = FindTable(file_number, file_size, &handle); + if (s.ok()) { + Table* t = reinterpret_cast(cache_->Value(handle))->table; + s = t->InternalGet(options, k, arg, saver); + cache_->Release(handle); + } + return s; +} + +void TableCache::Evict(uint64_t file_number) { + char buf[sizeof(file_number)]; + EncodeFixed64(buf, file_number); + cache_->Erase(Slice(buf, sizeof(buf))); +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/table_cache.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/table_cache.h new file mode 100644 index 0000000..8cf4aaf --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/table_cache.h @@ -0,0 +1,61 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Thread-safe (provides internal synchronization) + +#ifndef STORAGE_LEVELDB_DB_TABLE_CACHE_H_ +#define STORAGE_LEVELDB_DB_TABLE_CACHE_H_ + +#include +#include +#include "db/dbformat.h" +#include "leveldb/cache.h" +#include "leveldb/table.h" +#include "port/port.h" + +namespace leveldb { + +class Env; + +class TableCache { + public: + TableCache(const std::string& dbname, const Options* options, int entries); + ~TableCache(); + + // Return an iterator for the specified file number (the corresponding + // file length must be exactly "file_size" bytes). If "tableptr" is + // non-NULL, also sets "*tableptr" to point to the Table object + // underlying the returned iterator, or NULL if no Table object underlies + // the returned iterator. The returned "*tableptr" object is owned by + // the cache and should not be deleted, and is valid for as long as the + // returned iterator is live. + Iterator* NewIterator(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + Table** tableptr = NULL); + + // If a seek to internal key "k" in specified file finds an entry, + // call (*handle_result)(arg, found_key, found_value). + Status Get(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + const Slice& k, + void* arg, + void (*handle_result)(void*, const Slice&, const Slice&)); + + // Evict any entry for the specified file number + void Evict(uint64_t file_number); + + private: + Env* const env_; + const std::string dbname_; + const Options* options_; + Cache* cache_; + + Status FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle**); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_TABLE_CACHE_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_edit.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_edit.cc new file mode 100644 index 0000000..f10a2d5 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_edit.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_edit.h" + +#include "db/version_set.h" +#include "util/coding.h" + +namespace leveldb { + +// Tag numbers for serialized VersionEdit. These numbers are written to +// disk and should not be changed. +enum Tag { + kComparator = 1, + kLogNumber = 2, + kNextFileNumber = 3, + kLastSequence = 4, + kCompactPointer = 5, + kDeletedFile = 6, + kNewFile = 7, + // 8 was used for large value refs + kPrevLogNumber = 9 +}; + +void VersionEdit::Clear() { + comparator_.clear(); + log_number_ = 0; + prev_log_number_ = 0; + last_sequence_ = 0; + next_file_number_ = 0; + has_comparator_ = false; + has_log_number_ = false; + has_prev_log_number_ = false; + has_next_file_number_ = false; + has_last_sequence_ = false; + deleted_files_.clear(); + new_files_.clear(); +} + +void VersionEdit::EncodeTo(std::string* dst) const { + if (has_comparator_) { + PutVarint32(dst, kComparator); + PutLengthPrefixedSlice(dst, comparator_); + } + if (has_log_number_) { + PutVarint32(dst, kLogNumber); + PutVarint64(dst, log_number_); + } + if (has_prev_log_number_) { + PutVarint32(dst, kPrevLogNumber); + PutVarint64(dst, prev_log_number_); + } + if (has_next_file_number_) { + PutVarint32(dst, kNextFileNumber); + PutVarint64(dst, next_file_number_); + } + if (has_last_sequence_) { + PutVarint32(dst, kLastSequence); + PutVarint64(dst, last_sequence_); + } + + for (size_t i = 0; i < compact_pointers_.size(); i++) { + PutVarint32(dst, kCompactPointer); + PutVarint32(dst, compact_pointers_[i].first); // level + PutLengthPrefixedSlice(dst, compact_pointers_[i].second.Encode()); + } + + for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); + iter != deleted_files_.end(); + ++iter) { + PutVarint32(dst, kDeletedFile); + PutVarint32(dst, iter->first); // level + PutVarint64(dst, iter->second); // file number + } + + for (size_t i = 0; i < new_files_.size(); i++) { + const FileMetaData& f = new_files_[i].second; + PutVarint32(dst, kNewFile); + PutVarint32(dst, new_files_[i].first); // level + PutVarint64(dst, f.number); + PutVarint64(dst, f.file_size); + PutLengthPrefixedSlice(dst, f.smallest.Encode()); + PutLengthPrefixedSlice(dst, f.largest.Encode()); + } +} + +static bool GetInternalKey(Slice* input, InternalKey* dst) { + Slice str; + if (GetLengthPrefixedSlice(input, &str)) { + dst->DecodeFrom(str); + return true; + } else { + return false; + } +} + +static bool GetLevel(Slice* input, int* level) { + uint32_t v; + if (GetVarint32(input, &v) && + v < config::kNumLevels) { + *level = v; + return true; + } else { + return false; + } +} + +Status VersionEdit::DecodeFrom(const Slice& src) { + Clear(); + Slice input = src; + const char* msg = NULL; + uint32_t tag; + + // Temporary storage for parsing + int level; + uint64_t number; + FileMetaData f; + Slice str; + InternalKey key; + + while (msg == NULL && GetVarint32(&input, &tag)) { + switch (tag) { + case kComparator: + if (GetLengthPrefixedSlice(&input, &str)) { + comparator_ = str.ToString(); + has_comparator_ = true; + } else { + msg = "comparator name"; + } + break; + + case kLogNumber: + if (GetVarint64(&input, &log_number_)) { + has_log_number_ = true; + } else { + msg = "log number"; + } + break; + + case kPrevLogNumber: + if (GetVarint64(&input, &prev_log_number_)) { + has_prev_log_number_ = true; + } else { + msg = "previous log number"; + } + break; + + case kNextFileNumber: + if (GetVarint64(&input, &next_file_number_)) { + has_next_file_number_ = true; + } else { + msg = "next file number"; + } + break; + + case kLastSequence: + if (GetVarint64(&input, &last_sequence_)) { + has_last_sequence_ = true; + } else { + msg = "last sequence number"; + } + break; + + case kCompactPointer: + if (GetLevel(&input, &level) && + GetInternalKey(&input, &key)) { + compact_pointers_.push_back(std::make_pair(level, key)); + } else { + msg = "compaction pointer"; + } + break; + + case kDeletedFile: + if (GetLevel(&input, &level) && + GetVarint64(&input, &number)) { + deleted_files_.insert(std::make_pair(level, number)); + } else { + msg = "deleted file"; + } + break; + + case kNewFile: + if (GetLevel(&input, &level) && + GetVarint64(&input, &f.number) && + GetVarint64(&input, &f.file_size) && + GetInternalKey(&input, &f.smallest) && + GetInternalKey(&input, &f.largest)) { + new_files_.push_back(std::make_pair(level, f)); + } else { + msg = "new-file entry"; + } + break; + + default: + msg = "unknown tag"; + break; + } + } + + if (msg == NULL && !input.empty()) { + msg = "invalid tag"; + } + + Status result; + if (msg != NULL) { + result = Status::Corruption("VersionEdit", msg); + } + return result; +} + +std::string VersionEdit::DebugString() const { + std::string r; + r.append("VersionEdit {"); + if (has_comparator_) { + r.append("\n Comparator: "); + r.append(comparator_); + } + if (has_log_number_) { + r.append("\n LogNumber: "); + AppendNumberTo(&r, log_number_); + } + if (has_prev_log_number_) { + r.append("\n PrevLogNumber: "); + AppendNumberTo(&r, prev_log_number_); + } + if (has_next_file_number_) { + r.append("\n NextFile: "); + AppendNumberTo(&r, next_file_number_); + } + if (has_last_sequence_) { + r.append("\n LastSeq: "); + AppendNumberTo(&r, last_sequence_); + } + for (size_t i = 0; i < compact_pointers_.size(); i++) { + r.append("\n CompactPointer: "); + AppendNumberTo(&r, compact_pointers_[i].first); + r.append(" "); + r.append(compact_pointers_[i].second.DebugString()); + } + for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); + iter != deleted_files_.end(); + ++iter) { + r.append("\n DeleteFile: "); + AppendNumberTo(&r, iter->first); + r.append(" "); + AppendNumberTo(&r, iter->second); + } + for (size_t i = 0; i < new_files_.size(); i++) { + const FileMetaData& f = new_files_[i].second; + r.append("\n AddFile: "); + AppendNumberTo(&r, new_files_[i].first); + r.append(" "); + AppendNumberTo(&r, f.number); + r.append(" "); + AppendNumberTo(&r, f.file_size); + r.append(" "); + r.append(f.smallest.DebugString()); + r.append(" .. "); + r.append(f.largest.DebugString()); + } + r.append("\n}\n"); + return r; +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_edit.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_edit.h new file mode 100644 index 0000000..eaef77b --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_edit.h @@ -0,0 +1,107 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_VERSION_EDIT_H_ +#define STORAGE_LEVELDB_DB_VERSION_EDIT_H_ + +#include +#include +#include +#include "db/dbformat.h" + +namespace leveldb { + +class VersionSet; + +struct FileMetaData { + int refs; + int allowed_seeks; // Seeks allowed until compaction + uint64_t number; + uint64_t file_size; // File size in bytes + InternalKey smallest; // Smallest internal key served by table + InternalKey largest; // Largest internal key served by table + + FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) { } +}; + +class VersionEdit { + public: + VersionEdit() { Clear(); } + ~VersionEdit() { } + + void Clear(); + + void SetComparatorName(const Slice& name) { + has_comparator_ = true; + comparator_ = name.ToString(); + } + void SetLogNumber(uint64_t num) { + has_log_number_ = true; + log_number_ = num; + } + void SetPrevLogNumber(uint64_t num) { + has_prev_log_number_ = true; + prev_log_number_ = num; + } + void SetNextFile(uint64_t num) { + has_next_file_number_ = true; + next_file_number_ = num; + } + void SetLastSequence(SequenceNumber seq) { + has_last_sequence_ = true; + last_sequence_ = seq; + } + void SetCompactPointer(int level, const InternalKey& key) { + compact_pointers_.push_back(std::make_pair(level, key)); + } + + // Add the specified file at the specified number. + // REQUIRES: This version has not been saved (see VersionSet::SaveTo) + // REQUIRES: "smallest" and "largest" are smallest and largest keys in file + void AddFile(int level, uint64_t file, + uint64_t file_size, + const InternalKey& smallest, + const InternalKey& largest) { + FileMetaData f; + f.number = file; + f.file_size = file_size; + f.smallest = smallest; + f.largest = largest; + new_files_.push_back(std::make_pair(level, f)); + } + + // Delete the specified "file" from the specified "level". + void DeleteFile(int level, uint64_t file) { + deleted_files_.insert(std::make_pair(level, file)); + } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(const Slice& src); + + std::string DebugString() const; + + private: + friend class VersionSet; + + typedef std::set< std::pair > DeletedFileSet; + + std::string comparator_; + uint64_t log_number_; + uint64_t prev_log_number_; + uint64_t next_file_number_; + SequenceNumber last_sequence_; + bool has_comparator_; + bool has_log_number_; + bool has_prev_log_number_; + bool has_next_file_number_; + bool has_last_sequence_; + + std::vector< std::pair > compact_pointers_; + DeletedFileSet deleted_files_; + std::vector< std::pair > new_files_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_VERSION_EDIT_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_edit_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_edit_test.cc new file mode 100644 index 0000000..280310b --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_edit_test.cc @@ -0,0 +1,46 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_edit.h" +#include "util/testharness.h" + +namespace leveldb { + +static void TestEncodeDecode(const VersionEdit& edit) { + std::string encoded, encoded2; + edit.EncodeTo(&encoded); + VersionEdit parsed; + Status s = parsed.DecodeFrom(encoded); + ASSERT_TRUE(s.ok()) << s.ToString(); + parsed.EncodeTo(&encoded2); + ASSERT_EQ(encoded, encoded2); +} + +class VersionEditTest { }; + +TEST(VersionEditTest, EncodeDecode) { + static const uint64_t kBig = 1ull << 50; + + VersionEdit edit; + for (int i = 0; i < 4; i++) { + TestEncodeDecode(edit); + edit.AddFile(3, kBig + 300 + i, kBig + 400 + i, + InternalKey("foo", kBig + 500 + i, kTypeValue), + InternalKey("zoo", kBig + 600 + i, kTypeDeletion)); + edit.DeleteFile(4, kBig + 700 + i); + edit.SetCompactPointer(i, InternalKey("x", kBig + 900 + i, kTypeValue)); + } + + edit.SetComparatorName("foo"); + edit.SetLogNumber(kBig + 100); + edit.SetNextFile(kBig + 200); + edit.SetLastSequence(kBig + 1000); + TestEncodeDecode(edit); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_set.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_set.cc new file mode 100644 index 0000000..b1256f9 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_set.cc @@ -0,0 +1,1535 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_set.h" + +#include +#include +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "leveldb/env.h" +#include "leveldb/table_builder.h" +#include "table/merger.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +static int TargetFileSize(const Options* options) { + return options->max_file_size; +} + +// Maximum bytes of overlaps in grandparent (i.e., level+2) before we +// stop building a single file in a level->level+1 compaction. +static int64_t MaxGrandParentOverlapBytes(const Options* options) { + return 10 * TargetFileSize(options); +} + +// Maximum number of bytes in all compacted files. We avoid expanding +// the lower level file set of a compaction if it would make the +// total compaction cover more than this many bytes. +static int64_t ExpandedCompactionByteSizeLimit(const Options* options) { + return 25 * TargetFileSize(options); +} + +static double MaxBytesForLevel(const Options* options, int level) { + // Note: the result for level zero is not really used since we set + // the level-0 compaction threshold based on number of files. + + // Result for both level-0 and level-1 + double result = 10. * 1048576.0; + while (level > 1) { + result *= 10; + level--; + } + return result; +} + +static uint64_t MaxFileSizeForLevel(const Options* options, int level) { + // We could vary per level to reduce number of files? + return TargetFileSize(options); +} + +static int64_t TotalFileSize(const std::vector& files) { + int64_t sum = 0; + for (size_t i = 0; i < files.size(); i++) { + sum += files[i]->file_size; + } + return sum; +} + +Version::~Version() { + assert(refs_ == 0); + + // Remove from linked list + prev_->next_ = next_; + next_->prev_ = prev_; + + // Drop references to files + for (int level = 0; level < config::kNumLevels; level++) { + for (size_t i = 0; i < files_[level].size(); i++) { + FileMetaData* f = files_[level][i]; + assert(f->refs > 0); + f->refs--; + if (f->refs <= 0) { + delete f; + } + } + } +} + +int FindFile(const InternalKeyComparator& icmp, + const std::vector& files, + const Slice& key) { + uint32_t left = 0; + uint32_t right = files.size(); + while (left < right) { + uint32_t mid = (left + right) / 2; + const FileMetaData* f = files[mid]; + if (icmp.InternalKeyComparator::Compare(f->largest.Encode(), key) < 0) { + // Key at "mid.largest" is < "target". Therefore all + // files at or before "mid" are uninteresting. + left = mid + 1; + } else { + // Key at "mid.largest" is >= "target". Therefore all files + // after "mid" are uninteresting. + right = mid; + } + } + return right; +} + +static bool AfterFile(const Comparator* ucmp, + const Slice* user_key, const FileMetaData* f) { + // NULL user_key occurs before all keys and is therefore never after *f + return (user_key != NULL && + ucmp->Compare(*user_key, f->largest.user_key()) > 0); +} + +static bool BeforeFile(const Comparator* ucmp, + const Slice* user_key, const FileMetaData* f) { + // NULL user_key occurs after all keys and is therefore never before *f + return (user_key != NULL && + ucmp->Compare(*user_key, f->smallest.user_key()) < 0); +} + +bool SomeFileOverlapsRange( + const InternalKeyComparator& icmp, + bool disjoint_sorted_files, + const std::vector& files, + const Slice* smallest_user_key, + const Slice* largest_user_key) { + const Comparator* ucmp = icmp.user_comparator(); + if (!disjoint_sorted_files) { + // Need to check against all files + for (size_t i = 0; i < files.size(); i++) { + const FileMetaData* f = files[i]; + if (AfterFile(ucmp, smallest_user_key, f) || + BeforeFile(ucmp, largest_user_key, f)) { + // No overlap + } else { + return true; // Overlap + } + } + return false; + } + + // Binary search over file list + uint32_t index = 0; + if (smallest_user_key != NULL) { + // Find the earliest possible internal key for smallest_user_key + InternalKey small(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek); + index = FindFile(icmp, files, small.Encode()); + } + + if (index >= files.size()) { + // beginning of range is after all files, so no overlap. + return false; + } + + return !BeforeFile(ucmp, largest_user_key, files[index]); +} + +// An internal iterator. For a given version/level pair, yields +// information about the files in the level. For a given entry, key() +// is the largest key that occurs in the file, and value() is an +// 16-byte value containing the file number and file size, both +// encoded using EncodeFixed64. +class Version::LevelFileNumIterator : public Iterator { + public: + LevelFileNumIterator(const InternalKeyComparator& icmp, + const std::vector* flist) + : icmp_(icmp), + flist_(flist), + index_(flist->size()) { // Marks as invalid + } + virtual bool Valid() const { + return index_ < flist_->size(); + } + virtual void Seek(const Slice& target) { + index_ = FindFile(icmp_, *flist_, target); + } + virtual void SeekToFirst() { index_ = 0; } + virtual void SeekToLast() { + index_ = flist_->empty() ? 0 : flist_->size() - 1; + } + virtual void Next() { + assert(Valid()); + index_++; + } + virtual void Prev() { + assert(Valid()); + if (index_ == 0) { + index_ = flist_->size(); // Marks as invalid + } else { + index_--; + } + } + Slice key() const { + assert(Valid()); + return (*flist_)[index_]->largest.Encode(); + } + Slice value() const { + assert(Valid()); + EncodeFixed64(value_buf_, (*flist_)[index_]->number); + EncodeFixed64(value_buf_+8, (*flist_)[index_]->file_size); + return Slice(value_buf_, sizeof(value_buf_)); + } + virtual Status status() const { return Status::OK(); } + private: + const InternalKeyComparator icmp_; + const std::vector* const flist_; + uint32_t index_; + + // Backing store for value(). Holds the file number and size. + mutable char value_buf_[16]; +}; + +static Iterator* GetFileIterator(void* arg, + const ReadOptions& options, + const Slice& file_value) { + TableCache* cache = reinterpret_cast(arg); + if (file_value.size() != 16) { + return NewErrorIterator( + Status::Corruption("FileReader invoked with unexpected value")); + } else { + return cache->NewIterator(options, + DecodeFixed64(file_value.data()), + DecodeFixed64(file_value.data() + 8)); + } +} + +Iterator* Version::NewConcatenatingIterator(const ReadOptions& options, + int level) const { + return NewTwoLevelIterator( + new LevelFileNumIterator(vset_->icmp_, &files_[level]), + &GetFileIterator, vset_->table_cache_, options); +} + +void Version::AddIterators(const ReadOptions& options, + std::vector* iters) { + // Merge all level zero files together since they may overlap + for (size_t i = 0; i < files_[0].size(); i++) { + iters->push_back( + vset_->table_cache_->NewIterator( + options, files_[0][i]->number, files_[0][i]->file_size)); + } + + // For levels > 0, we can use a concatenating iterator that sequentially + // walks through the non-overlapping files in the level, opening them + // lazily. + for (int level = 1; level < config::kNumLevels; level++) { + if (!files_[level].empty()) { + iters->push_back(NewConcatenatingIterator(options, level)); + } + } +} + +// Callback from TableCache::Get() +namespace { +enum SaverState { + kNotFound, + kFound, + kDeleted, + kCorrupt, +}; +struct Saver { + SaverState state; + const Comparator* ucmp; + Slice user_key; + std::string* value; +}; +} +static void SaveValue(void* arg, const Slice& ikey, const Slice& v) { + Saver* s = reinterpret_cast(arg); + ParsedInternalKey parsed_key; + if (!ParseInternalKey(ikey, &parsed_key)) { + s->state = kCorrupt; + } else { + if (s->ucmp->Compare(parsed_key.user_key, s->user_key) == 0) { + s->state = (parsed_key.type == kTypeValue) ? kFound : kDeleted; + if (s->state == kFound) { + s->value->assign(v.data(), v.size()); + } + } + } +} + +static bool NewestFirst(FileMetaData* a, FileMetaData* b) { + return a->number > b->number; +} + +void Version::ForEachOverlapping(Slice user_key, Slice internal_key, + void* arg, + bool (*func)(void*, int, FileMetaData*)) { + // TODO(sanjay): Change Version::Get() to use this function. + const Comparator* ucmp = vset_->icmp_.user_comparator(); + + // Search level-0 in order from newest to oldest. + std::vector tmp; + tmp.reserve(files_[0].size()); + for (uint32_t i = 0; i < files_[0].size(); i++) { + FileMetaData* f = files_[0][i]; + if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 && + ucmp->Compare(user_key, f->largest.user_key()) <= 0) { + tmp.push_back(f); + } + } + if (!tmp.empty()) { + std::sort(tmp.begin(), tmp.end(), NewestFirst); + for (uint32_t i = 0; i < tmp.size(); i++) { + if (!(*func)(arg, 0, tmp[i])) { + return; + } + } + } + + // Search other levels. + for (int level = 1; level < config::kNumLevels; level++) { + size_t num_files = files_[level].size(); + if (num_files == 0) continue; + + // Binary search to find earliest index whose largest key >= internal_key. + uint32_t index = FindFile(vset_->icmp_, files_[level], internal_key); + if (index < num_files) { + FileMetaData* f = files_[level][index]; + if (ucmp->Compare(user_key, f->smallest.user_key()) < 0) { + // All of "f" is past any data for user_key + } else { + if (!(*func)(arg, level, f)) { + return; + } + } + } + } +} + +Status Version::Get(const ReadOptions& options, + const LookupKey& k, + std::string* value, + GetStats* stats) { + Slice ikey = k.internal_key(); + Slice user_key = k.user_key(); + const Comparator* ucmp = vset_->icmp_.user_comparator(); + Status s; + + stats->seek_file = NULL; + stats->seek_file_level = -1; + FileMetaData* last_file_read = NULL; + int last_file_read_level = -1; + + // We can search level-by-level since entries never hop across + // levels. Therefore we are guaranteed that if we find data + // in an smaller level, later levels are irrelevant. + std::vector tmp; + FileMetaData* tmp2; + for (int level = 0; level < config::kNumLevels; level++) { + size_t num_files = files_[level].size(); + if (num_files == 0) continue; + + // Get the list of files to search in this level + FileMetaData* const* files = &files_[level][0]; + if (level == 0) { + // Level-0 files may overlap each other. Find all files that + // overlap user_key and process them in order from newest to oldest. + tmp.reserve(num_files); + for (uint32_t i = 0; i < num_files; i++) { + FileMetaData* f = files[i]; + if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 && + ucmp->Compare(user_key, f->largest.user_key()) <= 0) { + tmp.push_back(f); + } + } + if (tmp.empty()) continue; + + std::sort(tmp.begin(), tmp.end(), NewestFirst); + files = &tmp[0]; + num_files = tmp.size(); + } else { + // Binary search to find earliest index whose largest key >= ikey. + uint32_t index = FindFile(vset_->icmp_, files_[level], ikey); + if (index >= num_files) { + files = NULL; + num_files = 0; + } else { + tmp2 = files[index]; + if (ucmp->Compare(user_key, tmp2->smallest.user_key()) < 0) { + // All of "tmp2" is past any data for user_key + files = NULL; + num_files = 0; + } else { + files = &tmp2; + num_files = 1; + } + } + } + + for (uint32_t i = 0; i < num_files; ++i) { + if (last_file_read != NULL && stats->seek_file == NULL) { + // We have had more than one seek for this read. Charge the 1st file. + stats->seek_file = last_file_read; + stats->seek_file_level = last_file_read_level; + } + + FileMetaData* f = files[i]; + last_file_read = f; + last_file_read_level = level; + + Saver saver; + saver.state = kNotFound; + saver.ucmp = ucmp; + saver.user_key = user_key; + saver.value = value; + s = vset_->table_cache_->Get(options, f->number, f->file_size, + ikey, &saver, SaveValue); + if (!s.ok()) { + return s; + } + switch (saver.state) { + case kNotFound: + break; // Keep searching in other files + case kFound: + return s; + case kDeleted: + s = Status::NotFound(Slice()); // Use empty error message for speed + return s; + case kCorrupt: + s = Status::Corruption("corrupted key for ", user_key); + return s; + } + } + } + + return Status::NotFound(Slice()); // Use an empty error message for speed +} + +bool Version::UpdateStats(const GetStats& stats) { + FileMetaData* f = stats.seek_file; + if (f != NULL) { + f->allowed_seeks--; + if (f->allowed_seeks <= 0 && file_to_compact_ == NULL) { + file_to_compact_ = f; + file_to_compact_level_ = stats.seek_file_level; + return true; + } + } + return false; +} + +bool Version::RecordReadSample(Slice internal_key) { + ParsedInternalKey ikey; + if (!ParseInternalKey(internal_key, &ikey)) { + return false; + } + + struct State { + GetStats stats; // Holds first matching file + int matches; + + static bool Match(void* arg, int level, FileMetaData* f) { + State* state = reinterpret_cast(arg); + state->matches++; + if (state->matches == 1) { + // Remember first match. + state->stats.seek_file = f; + state->stats.seek_file_level = level; + } + // We can stop iterating once we have a second match. + return state->matches < 2; + } + }; + + State state; + state.matches = 0; + ForEachOverlapping(ikey.user_key, internal_key, &state, &State::Match); + + // Must have at least two matches since we want to merge across + // files. But what if we have a single file that contains many + // overwrites and deletions? Should we have another mechanism for + // finding such files? + if (state.matches >= 2) { + // 1MB cost is about 1 seek (see comment in Builder::Apply). + return UpdateStats(state.stats); + } + return false; +} + +void Version::Ref() { + ++refs_; +} + +void Version::Unref() { + assert(this != &vset_->dummy_versions_); + assert(refs_ >= 1); + --refs_; + if (refs_ == 0) { + delete this; + } +} + +bool Version::OverlapInLevel(int level, + const Slice* smallest_user_key, + const Slice* largest_user_key) { + return SomeFileOverlapsRange(vset_->icmp_, (level > 0), files_[level], + smallest_user_key, largest_user_key); +} + +int Version::PickLevelForMemTableOutput( + const Slice& smallest_user_key, + const Slice& largest_user_key) { + int level = 0; + if (!OverlapInLevel(0, &smallest_user_key, &largest_user_key)) { + // Push to next level if there is no overlap in next level, + // and the #bytes overlapping in the level after that are limited. + InternalKey start(smallest_user_key, kMaxSequenceNumber, kValueTypeForSeek); + InternalKey limit(largest_user_key, 0, static_cast(0)); + std::vector overlaps; + while (level < config::kMaxMemCompactLevel) { + if (OverlapInLevel(level + 1, &smallest_user_key, &largest_user_key)) { + break; + } + if (level + 2 < config::kNumLevels) { + // Check that file does not overlap too many grandparent bytes. + GetOverlappingInputs(level + 2, &start, &limit, &overlaps); + const int64_t sum = TotalFileSize(overlaps); + if (sum > MaxGrandParentOverlapBytes(vset_->options_)) { + break; + } + } + level++; + } + } + return level; +} + +// Store in "*inputs" all files in "level" that overlap [begin,end] +void Version::GetOverlappingInputs( + int level, + const InternalKey* begin, + const InternalKey* end, + std::vector* inputs) { + assert(level >= 0); + assert(level < config::kNumLevels); + inputs->clear(); + Slice user_begin, user_end; + if (begin != NULL) { + user_begin = begin->user_key(); + } + if (end != NULL) { + user_end = end->user_key(); + } + const Comparator* user_cmp = vset_->icmp_.user_comparator(); + for (size_t i = 0; i < files_[level].size(); ) { + FileMetaData* f = files_[level][i++]; + const Slice file_start = f->smallest.user_key(); + const Slice file_limit = f->largest.user_key(); + if (begin != NULL && user_cmp->Compare(file_limit, user_begin) < 0) { + // "f" is completely before specified range; skip it + } else if (end != NULL && user_cmp->Compare(file_start, user_end) > 0) { + // "f" is completely after specified range; skip it + } else { + inputs->push_back(f); + if (level == 0) { + // Level-0 files may overlap each other. So check if the newly + // added file has expanded the range. If so, restart search. + if (begin != NULL && user_cmp->Compare(file_start, user_begin) < 0) { + user_begin = file_start; + inputs->clear(); + i = 0; + } else if (end != NULL && user_cmp->Compare(file_limit, user_end) > 0) { + user_end = file_limit; + inputs->clear(); + i = 0; + } + } + } + } +} + +std::string Version::DebugString() const { + std::string r; + for (int level = 0; level < config::kNumLevels; level++) { + // E.g., + // --- level 1 --- + // 17:123['a' .. 'd'] + // 20:43['e' .. 'g'] + r.append("--- level "); + AppendNumberTo(&r, level); + r.append(" ---\n"); + const std::vector& files = files_[level]; + for (size_t i = 0; i < files.size(); i++) { + r.push_back(' '); + AppendNumberTo(&r, files[i]->number); + r.push_back(':'); + AppendNumberTo(&r, files[i]->file_size); + r.append("["); + r.append(files[i]->smallest.DebugString()); + r.append(" .. "); + r.append(files[i]->largest.DebugString()); + r.append("]\n"); + } + } + return r; +} + +// A helper class so we can efficiently apply a whole sequence +// of edits to a particular state without creating intermediate +// Versions that contain full copies of the intermediate state. +class VersionSet::Builder { + private: + // Helper to sort by v->files_[file_number].smallest + struct BySmallestKey { + const InternalKeyComparator* internal_comparator; + + bool operator()(FileMetaData* f1, FileMetaData* f2) const { + int r = internal_comparator->Compare(f1->smallest, f2->smallest); + if (r != 0) { + return (r < 0); + } else { + // Break ties by file number + return (f1->number < f2->number); + } + } + }; + + typedef std::set FileSet; + struct LevelState { + std::set deleted_files; + FileSet* added_files; + }; + + VersionSet* vset_; + Version* base_; + LevelState levels_[config::kNumLevels]; + + public: + // Initialize a builder with the files from *base and other info from *vset + Builder(VersionSet* vset, Version* base) + : vset_(vset), + base_(base) { + base_->Ref(); + BySmallestKey cmp; + cmp.internal_comparator = &vset_->icmp_; + for (int level = 0; level < config::kNumLevels; level++) { + levels_[level].added_files = new FileSet(cmp); + } + } + + ~Builder() { + for (int level = 0; level < config::kNumLevels; level++) { + const FileSet* added = levels_[level].added_files; + std::vector to_unref; + to_unref.reserve(added->size()); + for (FileSet::const_iterator it = added->begin(); + it != added->end(); ++it) { + to_unref.push_back(*it); + } + delete added; + for (uint32_t i = 0; i < to_unref.size(); i++) { + FileMetaData* f = to_unref[i]; + f->refs--; + if (f->refs <= 0) { + delete f; + } + } + } + base_->Unref(); + } + + // Apply all of the edits in *edit to the current state. + void Apply(VersionEdit* edit) { + // Update compaction pointers + for (size_t i = 0; i < edit->compact_pointers_.size(); i++) { + const int level = edit->compact_pointers_[i].first; + vset_->compact_pointer_[level] = + edit->compact_pointers_[i].second.Encode().ToString(); + } + + // Delete files + const VersionEdit::DeletedFileSet& del = edit->deleted_files_; + for (VersionEdit::DeletedFileSet::const_iterator iter = del.begin(); + iter != del.end(); + ++iter) { + const int level = iter->first; + const uint64_t number = iter->second; + levels_[level].deleted_files.insert(number); + } + + // Add new files + for (size_t i = 0; i < edit->new_files_.size(); i++) { + const int level = edit->new_files_[i].first; + FileMetaData* f = new FileMetaData(edit->new_files_[i].second); + f->refs = 1; + + // We arrange to automatically compact this file after + // a certain number of seeks. Let's assume: + // (1) One seek costs 10ms + // (2) Writing or reading 1MB costs 10ms (100MB/s) + // (3) A compaction of 1MB does 25MB of IO: + // 1MB read from this level + // 10-12MB read from next level (boundaries may be misaligned) + // 10-12MB written to next level + // This implies that 25 seeks cost the same as the compaction + // of 1MB of data. I.e., one seek costs approximately the + // same as the compaction of 40KB of data. We are a little + // conservative and allow approximately one seek for every 16KB + // of data before triggering a compaction. + f->allowed_seeks = (f->file_size / 16384); + if (f->allowed_seeks < 100) f->allowed_seeks = 100; + + levels_[level].deleted_files.erase(f->number); + levels_[level].added_files->insert(f); + } + } + + // Save the current state in *v. + void SaveTo(Version* v) { + BySmallestKey cmp; + cmp.internal_comparator = &vset_->icmp_; + for (int level = 0; level < config::kNumLevels; level++) { + // Merge the set of added files with the set of pre-existing files. + // Drop any deleted files. Store the result in *v. + const std::vector& base_files = base_->files_[level]; + std::vector::const_iterator base_iter = base_files.begin(); + std::vector::const_iterator base_end = base_files.end(); + const FileSet* added = levels_[level].added_files; + v->files_[level].reserve(base_files.size() + added->size()); + for (FileSet::const_iterator added_iter = added->begin(); + added_iter != added->end(); + ++added_iter) { + // Add all smaller files listed in base_ + for (std::vector::const_iterator bpos + = std::upper_bound(base_iter, base_end, *added_iter, cmp); + base_iter != bpos; + ++base_iter) { + MaybeAddFile(v, level, *base_iter); + } + + MaybeAddFile(v, level, *added_iter); + } + + // Add remaining base files + for (; base_iter != base_end; ++base_iter) { + MaybeAddFile(v, level, *base_iter); + } + +#ifndef NDEBUG + // Make sure there is no overlap in levels > 0 + if (level > 0) { + for (uint32_t i = 1; i < v->files_[level].size(); i++) { + const InternalKey& prev_end = v->files_[level][i-1]->largest; + const InternalKey& this_begin = v->files_[level][i]->smallest; + if (vset_->icmp_.Compare(prev_end, this_begin) >= 0) { + fprintf(stderr, "overlapping ranges in same level %s vs. %s\n", + prev_end.DebugString().c_str(), + this_begin.DebugString().c_str()); + abort(); + } + } + } +#endif + } + } + + void MaybeAddFile(Version* v, int level, FileMetaData* f) { + if (levels_[level].deleted_files.count(f->number) > 0) { + // File is deleted: do nothing + } else { + std::vector* files = &v->files_[level]; + if (level > 0 && !files->empty()) { + // Must not overlap + assert(vset_->icmp_.Compare((*files)[files->size()-1]->largest, + f->smallest) < 0); + } + f->refs++; + files->push_back(f); + } + } +}; + +VersionSet::VersionSet(const std::string& dbname, + const Options* options, + TableCache* table_cache, + const InternalKeyComparator* cmp) + : env_(options->env), + dbname_(dbname), + options_(options), + table_cache_(table_cache), + icmp_(*cmp), + next_file_number_(2), + manifest_file_number_(0), // Filled by Recover() + last_sequence_(0), + log_number_(0), + prev_log_number_(0), + descriptor_file_(NULL), + descriptor_log_(NULL), + dummy_versions_(this), + current_(NULL) { + AppendVersion(new Version(this)); +} + +VersionSet::~VersionSet() { + current_->Unref(); + assert(dummy_versions_.next_ == &dummy_versions_); // List must be empty + delete descriptor_log_; + delete descriptor_file_; +} + +void VersionSet::AppendVersion(Version* v) { + // Make "v" current + assert(v->refs_ == 0); + assert(v != current_); + if (current_ != NULL) { + current_->Unref(); + } + current_ = v; + v->Ref(); + + // Append to linked list + v->prev_ = dummy_versions_.prev_; + v->next_ = &dummy_versions_; + v->prev_->next_ = v; + v->next_->prev_ = v; +} + +Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { + if (edit->has_log_number_) { + assert(edit->log_number_ >= log_number_); + assert(edit->log_number_ < next_file_number_); + } else { + edit->SetLogNumber(log_number_); + } + + if (!edit->has_prev_log_number_) { + edit->SetPrevLogNumber(prev_log_number_); + } + + edit->SetNextFile(next_file_number_); + edit->SetLastSequence(last_sequence_); + + Version* v = new Version(this); + { + Builder builder(this, current_); + builder.Apply(edit); + builder.SaveTo(v); + } + Finalize(v); + + // Initialize new descriptor log file if necessary by creating + // a temporary file that contains a snapshot of the current version. + std::string new_manifest_file; + Status s; + if (descriptor_log_ == NULL) { + // No reason to unlock *mu here since we only hit this path in the + // first call to LogAndApply (when opening the database). + assert(descriptor_file_ == NULL); + new_manifest_file = DescriptorFileName(dbname_, manifest_file_number_); + edit->SetNextFile(next_file_number_); + s = env_->NewWritableFile(new_manifest_file, &descriptor_file_); + if (s.ok()) { + descriptor_log_ = new log::Writer(descriptor_file_); + s = WriteSnapshot(descriptor_log_); + } + } + + // Unlock during expensive MANIFEST log write + { + mu->Unlock(); + + // Write new record to MANIFEST log + if (s.ok()) { + std::string record; + edit->EncodeTo(&record); + s = descriptor_log_->AddRecord(record); + if (s.ok()) { + s = descriptor_file_->Sync(); + } + if (!s.ok()) { + Log(options_->info_log, "MANIFEST write: %s\n", s.ToString().c_str()); + } + } + + // If we just created a new descriptor file, install it by writing a + // new CURRENT file that points to it. + if (s.ok() && !new_manifest_file.empty()) { + s = SetCurrentFile(env_, dbname_, manifest_file_number_); + } + + mu->Lock(); + } + + // Install the new version + if (s.ok()) { + AppendVersion(v); + log_number_ = edit->log_number_; + prev_log_number_ = edit->prev_log_number_; + } else { + delete v; + if (!new_manifest_file.empty()) { + delete descriptor_log_; + delete descriptor_file_; + descriptor_log_ = NULL; + descriptor_file_ = NULL; + env_->DeleteFile(new_manifest_file); + } + } + + return s; +} + +Status VersionSet::Recover(bool *save_manifest) { + struct LogReporter : public log::Reader::Reporter { + Status* status; + virtual void Corruption(size_t bytes, const Status& s) { + if (this->status->ok()) *this->status = s; + } + }; + + // Read "CURRENT" file, which contains a pointer to the current manifest file + std::string current; + Status s = ReadFileToString(env_, CurrentFileName(dbname_), ¤t); + if (!s.ok()) { + return s; + } + if (current.empty() || current[current.size()-1] != '\n') { + return Status::Corruption("CURRENT file does not end with newline"); + } + current.resize(current.size() - 1); + + std::string dscname = dbname_ + "/" + current; + SequentialFile* file; + s = env_->NewSequentialFile(dscname, &file); + if (!s.ok()) { + return s; + } + + bool have_log_number = false; + bool have_prev_log_number = false; + bool have_next_file = false; + bool have_last_sequence = false; + uint64_t next_file = 0; + uint64_t last_sequence = 0; + uint64_t log_number = 0; + uint64_t prev_log_number = 0; + Builder builder(this, current_); + + { + LogReporter reporter; + reporter.status = &s; + log::Reader reader(file, &reporter, true/*checksum*/, 0/*initial_offset*/); + Slice record; + std::string scratch; + while (reader.ReadRecord(&record, &scratch) && s.ok()) { + VersionEdit edit; + s = edit.DecodeFrom(record); + if (s.ok()) { + if (edit.has_comparator_ && + edit.comparator_ != icmp_.user_comparator()->Name()) { + s = Status::InvalidArgument( + edit.comparator_ + " does not match existing comparator ", + icmp_.user_comparator()->Name()); + } + } + + if (s.ok()) { + builder.Apply(&edit); + } + + if (edit.has_log_number_) { + log_number = edit.log_number_; + have_log_number = true; + } + + if (edit.has_prev_log_number_) { + prev_log_number = edit.prev_log_number_; + have_prev_log_number = true; + } + + if (edit.has_next_file_number_) { + next_file = edit.next_file_number_; + have_next_file = true; + } + + if (edit.has_last_sequence_) { + last_sequence = edit.last_sequence_; + have_last_sequence = true; + } + } + } + delete file; + file = NULL; + + if (s.ok()) { + if (!have_next_file) { + s = Status::Corruption("no meta-nextfile entry in descriptor"); + } else if (!have_log_number) { + s = Status::Corruption("no meta-lognumber entry in descriptor"); + } else if (!have_last_sequence) { + s = Status::Corruption("no last-sequence-number entry in descriptor"); + } + + if (!have_prev_log_number) { + prev_log_number = 0; + } + + MarkFileNumberUsed(prev_log_number); + MarkFileNumberUsed(log_number); + } + + if (s.ok()) { + Version* v = new Version(this); + builder.SaveTo(v); + // Install recovered version + Finalize(v); + AppendVersion(v); + manifest_file_number_ = next_file; + next_file_number_ = next_file + 1; + last_sequence_ = last_sequence; + log_number_ = log_number; + prev_log_number_ = prev_log_number; + + // See if we can reuse the existing MANIFEST file. + if (ReuseManifest(dscname, current)) { + // No need to save new manifest + } else { + *save_manifest = true; + } + } + + return s; +} + +bool VersionSet::ReuseManifest(const std::string& dscname, + const std::string& dscbase) { + if (!options_->reuse_logs) { + return false; + } + FileType manifest_type; + uint64_t manifest_number; + uint64_t manifest_size; + if (!ParseFileName(dscbase, &manifest_number, &manifest_type) || + manifest_type != kDescriptorFile || + !env_->GetFileSize(dscname, &manifest_size).ok() || + // Make new compacted MANIFEST if old one is too big + manifest_size >= TargetFileSize(options_)) { + return false; + } + + assert(descriptor_file_ == NULL); + assert(descriptor_log_ == NULL); + Status r = env_->NewAppendableFile(dscname, &descriptor_file_); + if (!r.ok()) { + Log(options_->info_log, "Reuse MANIFEST: %s\n", r.ToString().c_str()); + assert(descriptor_file_ == NULL); + return false; + } + + Log(options_->info_log, "Reusing MANIFEST %s\n", dscname.c_str()); + descriptor_log_ = new log::Writer(descriptor_file_, manifest_size); + manifest_file_number_ = manifest_number; + return true; +} + +void VersionSet::MarkFileNumberUsed(uint64_t number) { + if (next_file_number_ <= number) { + next_file_number_ = number + 1; + } +} + +void VersionSet::Finalize(Version* v) { + // Precomputed best level for next compaction + int best_level = -1; + double best_score = -1; + + for (int level = 0; level < config::kNumLevels-1; level++) { + double score; + if (level == 0) { + // We treat level-0 specially by bounding the number of files + // instead of number of bytes for two reasons: + // + // (1) With larger write-buffer sizes, it is nice not to do too + // many level-0 compactions. + // + // (2) The files in level-0 are merged on every read and + // therefore we wish to avoid too many files when the individual + // file size is small (perhaps because of a small write-buffer + // setting, or very high compression ratios, or lots of + // overwrites/deletions). + score = v->files_[level].size() / + static_cast(config::kL0_CompactionTrigger); + } else { + // Compute the ratio of current size to size limit. + const uint64_t level_bytes = TotalFileSize(v->files_[level]); + score = + static_cast(level_bytes) / MaxBytesForLevel(options_, level); + } + + if (score > best_score) { + best_level = level; + best_score = score; + } + } + + v->compaction_level_ = best_level; + v->compaction_score_ = best_score; +} + +Status VersionSet::WriteSnapshot(log::Writer* log) { + // TODO: Break up into multiple records to reduce memory usage on recovery? + + // Save metadata + VersionEdit edit; + edit.SetComparatorName(icmp_.user_comparator()->Name()); + + // Save compaction pointers + for (int level = 0; level < config::kNumLevels; level++) { + if (!compact_pointer_[level].empty()) { + InternalKey key; + key.DecodeFrom(compact_pointer_[level]); + edit.SetCompactPointer(level, key); + } + } + + // Save files + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector& files = current_->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + const FileMetaData* f = files[i]; + edit.AddFile(level, f->number, f->file_size, f->smallest, f->largest); + } + } + + std::string record; + edit.EncodeTo(&record); + return log->AddRecord(record); +} + +int VersionSet::NumLevelFiles(int level) const { + assert(level >= 0); + assert(level < config::kNumLevels); + return current_->files_[level].size(); +} + +const char* VersionSet::LevelSummary(LevelSummaryStorage* scratch) const { + // Update code if kNumLevels changes + assert(config::kNumLevels == 7); + snprintf(scratch->buffer, sizeof(scratch->buffer), + "files[ %d %d %d %d %d %d %d ]", + int(current_->files_[0].size()), + int(current_->files_[1].size()), + int(current_->files_[2].size()), + int(current_->files_[3].size()), + int(current_->files_[4].size()), + int(current_->files_[5].size()), + int(current_->files_[6].size())); + return scratch->buffer; +} + +uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) { + uint64_t result = 0; + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector& files = v->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + if (icmp_.Compare(files[i]->largest, ikey) <= 0) { + // Entire file is before "ikey", so just add the file size + result += files[i]->file_size; + } else if (icmp_.Compare(files[i]->smallest, ikey) > 0) { + // Entire file is after "ikey", so ignore + if (level > 0) { + // Files other than level 0 are sorted by meta->smallest, so + // no further files in this level will contain data for + // "ikey". + break; + } + } else { + // "ikey" falls in the range for this table. Add the + // approximate offset of "ikey" within the table. + Table* tableptr; + Iterator* iter = table_cache_->NewIterator( + ReadOptions(), files[i]->number, files[i]->file_size, &tableptr); + if (tableptr != NULL) { + result += tableptr->ApproximateOffsetOf(ikey.Encode()); + } + delete iter; + } + } + } + return result; +} + +void VersionSet::AddLiveFiles(std::set* live) { + for (Version* v = dummy_versions_.next_; + v != &dummy_versions_; + v = v->next_) { + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector& files = v->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + live->insert(files[i]->number); + } + } + } +} + +int64_t VersionSet::NumLevelBytes(int level) const { + assert(level >= 0); + assert(level < config::kNumLevels); + return TotalFileSize(current_->files_[level]); +} + +int64_t VersionSet::MaxNextLevelOverlappingBytes() { + int64_t result = 0; + std::vector overlaps; + for (int level = 1; level < config::kNumLevels - 1; level++) { + for (size_t i = 0; i < current_->files_[level].size(); i++) { + const FileMetaData* f = current_->files_[level][i]; + current_->GetOverlappingInputs(level+1, &f->smallest, &f->largest, + &overlaps); + const int64_t sum = TotalFileSize(overlaps); + if (sum > result) { + result = sum; + } + } + } + return result; +} + +// Stores the minimal range that covers all entries in inputs in +// *smallest, *largest. +// REQUIRES: inputs is not empty +void VersionSet::GetRange(const std::vector& inputs, + InternalKey* smallest, + InternalKey* largest) { + assert(!inputs.empty()); + smallest->Clear(); + largest->Clear(); + for (size_t i = 0; i < inputs.size(); i++) { + FileMetaData* f = inputs[i]; + if (i == 0) { + *smallest = f->smallest; + *largest = f->largest; + } else { + if (icmp_.Compare(f->smallest, *smallest) < 0) { + *smallest = f->smallest; + } + if (icmp_.Compare(f->largest, *largest) > 0) { + *largest = f->largest; + } + } + } +} + +// Stores the minimal range that covers all entries in inputs1 and inputs2 +// in *smallest, *largest. +// REQUIRES: inputs is not empty +void VersionSet::GetRange2(const std::vector& inputs1, + const std::vector& inputs2, + InternalKey* smallest, + InternalKey* largest) { + std::vector all = inputs1; + all.insert(all.end(), inputs2.begin(), inputs2.end()); + GetRange(all, smallest, largest); +} + +Iterator* VersionSet::MakeInputIterator(Compaction* c) { + ReadOptions options; + options.verify_checksums = options_->paranoid_checks; + options.fill_cache = false; + + // Level-0 files have to be merged together. For other levels, + // we will make a concatenating iterator per level. + // TODO(opt): use concatenating iterator for level-0 if there is no overlap + const int space = (c->level() == 0 ? c->inputs_[0].size() + 1 : 2); + Iterator** list = new Iterator*[space]; + int num = 0; + for (int which = 0; which < 2; which++) { + if (!c->inputs_[which].empty()) { + if (c->level() + which == 0) { + const std::vector& files = c->inputs_[which]; + for (size_t i = 0; i < files.size(); i++) { + list[num++] = table_cache_->NewIterator( + options, files[i]->number, files[i]->file_size); + } + } else { + // Create concatenating iterator for the files from this level + list[num++] = NewTwoLevelIterator( + new Version::LevelFileNumIterator(icmp_, &c->inputs_[which]), + &GetFileIterator, table_cache_, options); + } + } + } + assert(num <= space); + Iterator* result = NewMergingIterator(&icmp_, list, num); + delete[] list; + return result; +} + +Compaction* VersionSet::PickCompaction() { + Compaction* c; + int level; + + // We prefer compactions triggered by too much data in a level over + // the compactions triggered by seeks. + const bool size_compaction = (current_->compaction_score_ >= 1); + const bool seek_compaction = (current_->file_to_compact_ != NULL); + if (size_compaction) { + level = current_->compaction_level_; + assert(level >= 0); + assert(level+1 < config::kNumLevels); + c = new Compaction(options_, level); + + // Pick the first file that comes after compact_pointer_[level] + for (size_t i = 0; i < current_->files_[level].size(); i++) { + FileMetaData* f = current_->files_[level][i]; + if (compact_pointer_[level].empty() || + icmp_.Compare(f->largest.Encode(), compact_pointer_[level]) > 0) { + c->inputs_[0].push_back(f); + break; + } + } + if (c->inputs_[0].empty()) { + // Wrap-around to the beginning of the key space + c->inputs_[0].push_back(current_->files_[level][0]); + } + } else if (seek_compaction) { + level = current_->file_to_compact_level_; + c = new Compaction(options_, level); + c->inputs_[0].push_back(current_->file_to_compact_); + } else { + return NULL; + } + + c->input_version_ = current_; + c->input_version_->Ref(); + + // Files in level 0 may overlap each other, so pick up all overlapping ones + if (level == 0) { + InternalKey smallest, largest; + GetRange(c->inputs_[0], &smallest, &largest); + // Note that the next call will discard the file we placed in + // c->inputs_[0] earlier and replace it with an overlapping set + // which will include the picked file. + current_->GetOverlappingInputs(0, &smallest, &largest, &c->inputs_[0]); + assert(!c->inputs_[0].empty()); + } + + SetupOtherInputs(c); + + return c; +} + +void VersionSet::SetupOtherInputs(Compaction* c) { + const int level = c->level(); + InternalKey smallest, largest; + GetRange(c->inputs_[0], &smallest, &largest); + + current_->GetOverlappingInputs(level+1, &smallest, &largest, &c->inputs_[1]); + + // Get entire range covered by compaction + InternalKey all_start, all_limit; + GetRange2(c->inputs_[0], c->inputs_[1], &all_start, &all_limit); + + // See if we can grow the number of inputs in "level" without + // changing the number of "level+1" files we pick up. + if (!c->inputs_[1].empty()) { + std::vector expanded0; + current_->GetOverlappingInputs(level, &all_start, &all_limit, &expanded0); + const int64_t inputs0_size = TotalFileSize(c->inputs_[0]); + const int64_t inputs1_size = TotalFileSize(c->inputs_[1]); + const int64_t expanded0_size = TotalFileSize(expanded0); + if (expanded0.size() > c->inputs_[0].size() && + inputs1_size + expanded0_size < + ExpandedCompactionByteSizeLimit(options_)) { + InternalKey new_start, new_limit; + GetRange(expanded0, &new_start, &new_limit); + std::vector expanded1; + current_->GetOverlappingInputs(level+1, &new_start, &new_limit, + &expanded1); + if (expanded1.size() == c->inputs_[1].size()) { + Log(options_->info_log, + "Expanding@%d %d+%d (%ld+%ld bytes) to %d+%d (%ld+%ld bytes)\n", + level, + int(c->inputs_[0].size()), + int(c->inputs_[1].size()), + long(inputs0_size), long(inputs1_size), + int(expanded0.size()), + int(expanded1.size()), + long(expanded0_size), long(inputs1_size)); + smallest = new_start; + largest = new_limit; + c->inputs_[0] = expanded0; + c->inputs_[1] = expanded1; + GetRange2(c->inputs_[0], c->inputs_[1], &all_start, &all_limit); + } + } + } + + // Compute the set of grandparent files that overlap this compaction + // (parent == level+1; grandparent == level+2) + if (level + 2 < config::kNumLevels) { + current_->GetOverlappingInputs(level + 2, &all_start, &all_limit, + &c->grandparents_); + } + + if (false) { + Log(options_->info_log, "Compacting %d '%s' .. '%s'", + level, + smallest.DebugString().c_str(), + largest.DebugString().c_str()); + } + + // Update the place where we will do the next compaction for this level. + // We update this immediately instead of waiting for the VersionEdit + // to be applied so that if the compaction fails, we will try a different + // key range next time. + compact_pointer_[level] = largest.Encode().ToString(); + c->edit_.SetCompactPointer(level, largest); +} + +Compaction* VersionSet::CompactRange( + int level, + const InternalKey* begin, + const InternalKey* end) { + std::vector inputs; + current_->GetOverlappingInputs(level, begin, end, &inputs); + if (inputs.empty()) { + return NULL; + } + + // Avoid compacting too much in one shot in case the range is large. + // But we cannot do this for level-0 since level-0 files can overlap + // and we must not pick one file and drop another older file if the + // two files overlap. + if (level > 0) { + const uint64_t limit = MaxFileSizeForLevel(options_, level); + uint64_t total = 0; + for (size_t i = 0; i < inputs.size(); i++) { + uint64_t s = inputs[i]->file_size; + total += s; + if (total >= limit) { + inputs.resize(i + 1); + break; + } + } + } + + Compaction* c = new Compaction(options_, level); + c->input_version_ = current_; + c->input_version_->Ref(); + c->inputs_[0] = inputs; + SetupOtherInputs(c); + return c; +} + +Compaction::Compaction(const Options* options, int level) + : level_(level), + max_output_file_size_(MaxFileSizeForLevel(options, level)), + input_version_(NULL), + grandparent_index_(0), + seen_key_(false), + overlapped_bytes_(0) { + for (int i = 0; i < config::kNumLevels; i++) { + level_ptrs_[i] = 0; + } +} + +Compaction::~Compaction() { + if (input_version_ != NULL) { + input_version_->Unref(); + } +} + +bool Compaction::IsTrivialMove() const { + const VersionSet* vset = input_version_->vset_; + // Avoid a move if there is lots of overlapping grandparent data. + // Otherwise, the move could create a parent file that will require + // a very expensive merge later on. + return (num_input_files(0) == 1 && num_input_files(1) == 0 && + TotalFileSize(grandparents_) <= + MaxGrandParentOverlapBytes(vset->options_)); +} + +void Compaction::AddInputDeletions(VersionEdit* edit) { + for (int which = 0; which < 2; which++) { + for (size_t i = 0; i < inputs_[which].size(); i++) { + edit->DeleteFile(level_ + which, inputs_[which][i]->number); + } + } +} + +bool Compaction::IsBaseLevelForKey(const Slice& user_key) { + // Maybe use binary search to find right entry instead of linear search? + const Comparator* user_cmp = input_version_->vset_->icmp_.user_comparator(); + for (int lvl = level_ + 2; lvl < config::kNumLevels; lvl++) { + const std::vector& files = input_version_->files_[lvl]; + for (; level_ptrs_[lvl] < files.size(); ) { + FileMetaData* f = files[level_ptrs_[lvl]]; + if (user_cmp->Compare(user_key, f->largest.user_key()) <= 0) { + // We've advanced far enough + if (user_cmp->Compare(user_key, f->smallest.user_key()) >= 0) { + // Key falls in this file's range, so definitely not base level + return false; + } + break; + } + level_ptrs_[lvl]++; + } + } + return true; +} + +bool Compaction::ShouldStopBefore(const Slice& internal_key) { + const VersionSet* vset = input_version_->vset_; + // Scan to find earliest grandparent file that contains key. + const InternalKeyComparator* icmp = &vset->icmp_; + while (grandparent_index_ < grandparents_.size() && + icmp->Compare(internal_key, + grandparents_[grandparent_index_]->largest.Encode()) > 0) { + if (seen_key_) { + overlapped_bytes_ += grandparents_[grandparent_index_]->file_size; + } + grandparent_index_++; + } + seen_key_ = true; + + if (overlapped_bytes_ > MaxGrandParentOverlapBytes(vset->options_)) { + // Too much overlap for current output; start new output + overlapped_bytes_ = 0; + return true; + } else { + return false; + } +} + +void Compaction::ReleaseInputs() { + if (input_version_ != NULL) { + input_version_->Unref(); + input_version_ = NULL; + } +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_set.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_set.h new file mode 100644 index 0000000..c4e7ac3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_set.h @@ -0,0 +1,398 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// The representation of a DBImpl consists of a set of Versions. The +// newest version is called "current". Older versions may be kept +// around to provide a consistent view to live iterators. +// +// Each Version keeps track of a set of Table files per level. The +// entire set of versions is maintained in a VersionSet. +// +// Version,VersionSet are thread-compatible, but require external +// synchronization on all accesses. + +#ifndef STORAGE_LEVELDB_DB_VERSION_SET_H_ +#define STORAGE_LEVELDB_DB_VERSION_SET_H_ + +#include +#include +#include +#include "db/dbformat.h" +#include "db/version_edit.h" +#include "port/port.h" +#include "port/thread_annotations.h" + +namespace leveldb { + +namespace log { class Writer; } + +class Compaction; +class Iterator; +class MemTable; +class TableBuilder; +class TableCache; +class Version; +class VersionSet; +class WritableFile; + +// Return the smallest index i such that files[i]->largest >= key. +// Return files.size() if there is no such file. +// REQUIRES: "files" contains a sorted list of non-overlapping files. +extern int FindFile(const InternalKeyComparator& icmp, + const std::vector& files, + const Slice& key); + +// Returns true iff some file in "files" overlaps the user key range +// [*smallest,*largest]. +// smallest==NULL represents a key smaller than all keys in the DB. +// largest==NULL represents a key largest than all keys in the DB. +// REQUIRES: If disjoint_sorted_files, files[] contains disjoint ranges +// in sorted order. +extern bool SomeFileOverlapsRange( + const InternalKeyComparator& icmp, + bool disjoint_sorted_files, + const std::vector& files, + const Slice* smallest_user_key, + const Slice* largest_user_key); + +class Version { + public: + // Append to *iters a sequence of iterators that will + // yield the contents of this Version when merged together. + // REQUIRES: This version has been saved (see VersionSet::SaveTo) + void AddIterators(const ReadOptions&, std::vector* iters); + + // Lookup the value for key. If found, store it in *val and + // return OK. Else return a non-OK status. Fills *stats. + // REQUIRES: lock is not held + struct GetStats { + FileMetaData* seek_file; + int seek_file_level; + }; + Status Get(const ReadOptions&, const LookupKey& key, std::string* val, + GetStats* stats); + + // Adds "stats" into the current state. Returns true if a new + // compaction may need to be triggered, false otherwise. + // REQUIRES: lock is held + bool UpdateStats(const GetStats& stats); + + // Record a sample of bytes read at the specified internal key. + // Samples are taken approximately once every config::kReadBytesPeriod + // bytes. Returns true if a new compaction may need to be triggered. + // REQUIRES: lock is held + bool RecordReadSample(Slice key); + + // Reference count management (so Versions do not disappear out from + // under live iterators) + void Ref(); + void Unref(); + + void GetOverlappingInputs( + int level, + const InternalKey* begin, // NULL means before all keys + const InternalKey* end, // NULL means after all keys + std::vector* inputs); + + // Returns true iff some file in the specified level overlaps + // some part of [*smallest_user_key,*largest_user_key]. + // smallest_user_key==NULL represents a key smaller than all keys in the DB. + // largest_user_key==NULL represents a key largest than all keys in the DB. + bool OverlapInLevel(int level, + const Slice* smallest_user_key, + const Slice* largest_user_key); + + // Return the level at which we should place a new memtable compaction + // result that covers the range [smallest_user_key,largest_user_key]. + int PickLevelForMemTableOutput(const Slice& smallest_user_key, + const Slice& largest_user_key); + + int NumFiles(int level) const { return files_[level].size(); } + + // Return a human readable string that describes this version's contents. + std::string DebugString() const; + + private: + friend class Compaction; + friend class VersionSet; + + class LevelFileNumIterator; + Iterator* NewConcatenatingIterator(const ReadOptions&, int level) const; + + // Call func(arg, level, f) for every file that overlaps user_key in + // order from newest to oldest. If an invocation of func returns + // false, makes no more calls. + // + // REQUIRES: user portion of internal_key == user_key. + void ForEachOverlapping(Slice user_key, Slice internal_key, + void* arg, + bool (*func)(void*, int, FileMetaData*)); + + VersionSet* vset_; // VersionSet to which this Version belongs + Version* next_; // Next version in linked list + Version* prev_; // Previous version in linked list + int refs_; // Number of live refs to this version + + // List of files per level + std::vector files_[config::kNumLevels]; + + // Next file to compact based on seek stats. + FileMetaData* file_to_compact_; + int file_to_compact_level_; + + // Level that should be compacted next and its compaction score. + // Score < 1 means compaction is not strictly needed. These fields + // are initialized by Finalize(). + double compaction_score_; + int compaction_level_; + + explicit Version(VersionSet* vset) + : vset_(vset), next_(this), prev_(this), refs_(0), + file_to_compact_(NULL), + file_to_compact_level_(-1), + compaction_score_(-1), + compaction_level_(-1) { + } + + ~Version(); + + // No copying allowed + Version(const Version&); + void operator=(const Version&); +}; + +class VersionSet { + public: + VersionSet(const std::string& dbname, + const Options* options, + TableCache* table_cache, + const InternalKeyComparator*); + ~VersionSet(); + + // Apply *edit to the current version to form a new descriptor that + // is both saved to persistent state and installed as the new + // current version. Will release *mu while actually writing to the file. + // REQUIRES: *mu is held on entry. + // REQUIRES: no other thread concurrently calls LogAndApply() + Status LogAndApply(VersionEdit* edit, port::Mutex* mu) + EXCLUSIVE_LOCKS_REQUIRED(mu); + + // Recover the last saved descriptor from persistent storage. + Status Recover(bool *save_manifest); + + // Return the current version. + Version* current() const { return current_; } + + // Return the current manifest file number + uint64_t ManifestFileNumber() const { return manifest_file_number_; } + + // Allocate and return a new file number + uint64_t NewFileNumber() { return next_file_number_++; } + + // Arrange to reuse "file_number" unless a newer file number has + // already been allocated. + // REQUIRES: "file_number" was returned by a call to NewFileNumber(). + void ReuseFileNumber(uint64_t file_number) { + if (next_file_number_ == file_number + 1) { + next_file_number_ = file_number; + } + } + + // Return the number of Table files at the specified level. + int NumLevelFiles(int level) const; + + // Return the combined file size of all files at the specified level. + int64_t NumLevelBytes(int level) const; + + // Return the last sequence number. + uint64_t LastSequence() const { return last_sequence_; } + + // Set the last sequence number to s. + void SetLastSequence(uint64_t s) { + assert(s >= last_sequence_); + last_sequence_ = s; + } + + // Mark the specified file number as used. + void MarkFileNumberUsed(uint64_t number); + + // Return the current log file number. + uint64_t LogNumber() const { return log_number_; } + + // Return the log file number for the log file that is currently + // being compacted, or zero if there is no such log file. + uint64_t PrevLogNumber() const { return prev_log_number_; } + + // Pick level and inputs for a new compaction. + // Returns NULL if there is no compaction to be done. + // Otherwise returns a pointer to a heap-allocated object that + // describes the compaction. Caller should delete the result. + Compaction* PickCompaction(); + + // Return a compaction object for compacting the range [begin,end] in + // the specified level. Returns NULL if there is nothing in that + // level that overlaps the specified range. Caller should delete + // the result. + Compaction* CompactRange( + int level, + const InternalKey* begin, + const InternalKey* end); + + // Return the maximum overlapping data (in bytes) at next level for any + // file at a level >= 1. + int64_t MaxNextLevelOverlappingBytes(); + + // Create an iterator that reads over the compaction inputs for "*c". + // The caller should delete the iterator when no longer needed. + Iterator* MakeInputIterator(Compaction* c); + + // Returns true iff some level needs a compaction. + bool NeedsCompaction() const { + Version* v = current_; + return (v->compaction_score_ >= 1) || (v->file_to_compact_ != NULL); + } + + // Add all files listed in any live version to *live. + // May also mutate some internal state. + void AddLiveFiles(std::set* live); + + // Return the approximate offset in the database of the data for + // "key" as of version "v". + uint64_t ApproximateOffsetOf(Version* v, const InternalKey& key); + + // Return a human-readable short (single-line) summary of the number + // of files per level. Uses *scratch as backing store. + struct LevelSummaryStorage { + char buffer[100]; + }; + const char* LevelSummary(LevelSummaryStorage* scratch) const; + + private: + class Builder; + + friend class Compaction; + friend class Version; + + bool ReuseManifest(const std::string& dscname, const std::string& dscbase); + + void Finalize(Version* v); + + void GetRange(const std::vector& inputs, + InternalKey* smallest, + InternalKey* largest); + + void GetRange2(const std::vector& inputs1, + const std::vector& inputs2, + InternalKey* smallest, + InternalKey* largest); + + void SetupOtherInputs(Compaction* c); + + // Save current contents to *log + Status WriteSnapshot(log::Writer* log); + + void AppendVersion(Version* v); + + Env* const env_; + const std::string dbname_; + const Options* const options_; + TableCache* const table_cache_; + const InternalKeyComparator icmp_; + uint64_t next_file_number_; + uint64_t manifest_file_number_; + uint64_t last_sequence_; + uint64_t log_number_; + uint64_t prev_log_number_; // 0 or backing store for memtable being compacted + + // Opened lazily + WritableFile* descriptor_file_; + log::Writer* descriptor_log_; + Version dummy_versions_; // Head of circular doubly-linked list of versions. + Version* current_; // == dummy_versions_.prev_ + + // Per-level key at which the next compaction at that level should start. + // Either an empty string, or a valid InternalKey. + std::string compact_pointer_[config::kNumLevels]; + + // No copying allowed + VersionSet(const VersionSet&); + void operator=(const VersionSet&); +}; + +// A Compaction encapsulates information about a compaction. +class Compaction { + public: + ~Compaction(); + + // Return the level that is being compacted. Inputs from "level" + // and "level+1" will be merged to produce a set of "level+1" files. + int level() const { return level_; } + + // Return the object that holds the edits to the descriptor done + // by this compaction. + VersionEdit* edit() { return &edit_; } + + // "which" must be either 0 or 1 + int num_input_files(int which) const { return inputs_[which].size(); } + + // Return the ith input file at "level()+which" ("which" must be 0 or 1). + FileMetaData* input(int which, int i) const { return inputs_[which][i]; } + + // Maximum size of files to build during this compaction. + uint64_t MaxOutputFileSize() const { return max_output_file_size_; } + + // Is this a trivial compaction that can be implemented by just + // moving a single input file to the next level (no merging or splitting) + bool IsTrivialMove() const; + + // Add all inputs to this compaction as delete operations to *edit. + void AddInputDeletions(VersionEdit* edit); + + // Returns true if the information we have available guarantees that + // the compaction is producing data in "level+1" for which no data exists + // in levels greater than "level+1". + bool IsBaseLevelForKey(const Slice& user_key); + + // Returns true iff we should stop building the current output + // before processing "internal_key". + bool ShouldStopBefore(const Slice& internal_key); + + // Release the input version for the compaction, once the compaction + // is successful. + void ReleaseInputs(); + + private: + friend class Version; + friend class VersionSet; + + Compaction(const Options* options, int level); + + int level_; + uint64_t max_output_file_size_; + Version* input_version_; + VersionEdit edit_; + + // Each compaction reads inputs from "level_" and "level_+1" + std::vector inputs_[2]; // The two sets of inputs + + // State used to check for number of of overlapping grandparent files + // (parent == level_ + 1, grandparent == level_ + 2) + std::vector grandparents_; + size_t grandparent_index_; // Index in grandparent_starts_ + bool seen_key_; // Some output key has been seen + int64_t overlapped_bytes_; // Bytes of overlap between current output + // and grandparent files + + // State for implementing IsBaseLevelForKey + + // level_ptrs_ holds indices into input_version_->levels_: our state + // is that we are positioned at one of the file ranges for each + // higher level than the ones involved in this compaction (i.e. for + // all L >= level_ + 2). + size_t level_ptrs_[config::kNumLevels]; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_VERSION_SET_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_set_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_set_test.cc new file mode 100644 index 0000000..501e34d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_set_test.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_set.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class FindFileTest { + public: + std::vector files_; + bool disjoint_sorted_files_; + + FindFileTest() : disjoint_sorted_files_(true) { } + + ~FindFileTest() { + for (int i = 0; i < files_.size(); i++) { + delete files_[i]; + } + } + + void Add(const char* smallest, const char* largest, + SequenceNumber smallest_seq = 100, + SequenceNumber largest_seq = 100) { + FileMetaData* f = new FileMetaData; + f->number = files_.size() + 1; + f->smallest = InternalKey(smallest, smallest_seq, kTypeValue); + f->largest = InternalKey(largest, largest_seq, kTypeValue); + files_.push_back(f); + } + + int Find(const char* key) { + InternalKey target(key, 100, kTypeValue); + InternalKeyComparator cmp(BytewiseComparator()); + return FindFile(cmp, files_, target.Encode()); + } + + bool Overlaps(const char* smallest, const char* largest) { + InternalKeyComparator cmp(BytewiseComparator()); + Slice s(smallest != NULL ? smallest : ""); + Slice l(largest != NULL ? largest : ""); + return SomeFileOverlapsRange(cmp, disjoint_sorted_files_, files_, + (smallest != NULL ? &s : NULL), + (largest != NULL ? &l : NULL)); + } +}; + +TEST(FindFileTest, Empty) { + ASSERT_EQ(0, Find("foo")); + ASSERT_TRUE(! Overlaps("a", "z")); + ASSERT_TRUE(! Overlaps(NULL, "z")); + ASSERT_TRUE(! Overlaps("a", NULL)); + ASSERT_TRUE(! Overlaps(NULL, NULL)); +} + +TEST(FindFileTest, Single) { + Add("p", "q"); + ASSERT_EQ(0, Find("a")); + ASSERT_EQ(0, Find("p")); + ASSERT_EQ(0, Find("p1")); + ASSERT_EQ(0, Find("q")); + ASSERT_EQ(1, Find("q1")); + ASSERT_EQ(1, Find("z")); + + ASSERT_TRUE(! Overlaps("a", "b")); + ASSERT_TRUE(! Overlaps("z1", "z2")); + ASSERT_TRUE(Overlaps("a", "p")); + ASSERT_TRUE(Overlaps("a", "q")); + ASSERT_TRUE(Overlaps("a", "z")); + ASSERT_TRUE(Overlaps("p", "p1")); + ASSERT_TRUE(Overlaps("p", "q")); + ASSERT_TRUE(Overlaps("p", "z")); + ASSERT_TRUE(Overlaps("p1", "p2")); + ASSERT_TRUE(Overlaps("p1", "z")); + ASSERT_TRUE(Overlaps("q", "q")); + ASSERT_TRUE(Overlaps("q", "q1")); + + ASSERT_TRUE(! Overlaps(NULL, "j")); + ASSERT_TRUE(! Overlaps("r", NULL)); + ASSERT_TRUE(Overlaps(NULL, "p")); + ASSERT_TRUE(Overlaps(NULL, "p1")); + ASSERT_TRUE(Overlaps("q", NULL)); + ASSERT_TRUE(Overlaps(NULL, NULL)); +} + + +TEST(FindFileTest, Multiple) { + Add("150", "200"); + Add("200", "250"); + Add("300", "350"); + Add("400", "450"); + ASSERT_EQ(0, Find("100")); + ASSERT_EQ(0, Find("150")); + ASSERT_EQ(0, Find("151")); + ASSERT_EQ(0, Find("199")); + ASSERT_EQ(0, Find("200")); + ASSERT_EQ(1, Find("201")); + ASSERT_EQ(1, Find("249")); + ASSERT_EQ(1, Find("250")); + ASSERT_EQ(2, Find("251")); + ASSERT_EQ(2, Find("299")); + ASSERT_EQ(2, Find("300")); + ASSERT_EQ(2, Find("349")); + ASSERT_EQ(2, Find("350")); + ASSERT_EQ(3, Find("351")); + ASSERT_EQ(3, Find("400")); + ASSERT_EQ(3, Find("450")); + ASSERT_EQ(4, Find("451")); + + ASSERT_TRUE(! Overlaps("100", "149")); + ASSERT_TRUE(! Overlaps("251", "299")); + ASSERT_TRUE(! Overlaps("451", "500")); + ASSERT_TRUE(! Overlaps("351", "399")); + + ASSERT_TRUE(Overlaps("100", "150")); + ASSERT_TRUE(Overlaps("100", "200")); + ASSERT_TRUE(Overlaps("100", "300")); + ASSERT_TRUE(Overlaps("100", "400")); + ASSERT_TRUE(Overlaps("100", "500")); + ASSERT_TRUE(Overlaps("375", "400")); + ASSERT_TRUE(Overlaps("450", "450")); + ASSERT_TRUE(Overlaps("450", "500")); +} + +TEST(FindFileTest, MultipleNullBoundaries) { + Add("150", "200"); + Add("200", "250"); + Add("300", "350"); + Add("400", "450"); + ASSERT_TRUE(! Overlaps(NULL, "149")); + ASSERT_TRUE(! Overlaps("451", NULL)); + ASSERT_TRUE(Overlaps(NULL, NULL)); + ASSERT_TRUE(Overlaps(NULL, "150")); + ASSERT_TRUE(Overlaps(NULL, "199")); + ASSERT_TRUE(Overlaps(NULL, "200")); + ASSERT_TRUE(Overlaps(NULL, "201")); + ASSERT_TRUE(Overlaps(NULL, "400")); + ASSERT_TRUE(Overlaps(NULL, "800")); + ASSERT_TRUE(Overlaps("100", NULL)); + ASSERT_TRUE(Overlaps("200", NULL)); + ASSERT_TRUE(Overlaps("449", NULL)); + ASSERT_TRUE(Overlaps("450", NULL)); +} + +TEST(FindFileTest, OverlapSequenceChecks) { + Add("200", "200", 5000, 3000); + ASSERT_TRUE(! Overlaps("199", "199")); + ASSERT_TRUE(! Overlaps("201", "300")); + ASSERT_TRUE(Overlaps("200", "200")); + ASSERT_TRUE(Overlaps("190", "200")); + ASSERT_TRUE(Overlaps("200", "210")); +} + +TEST(FindFileTest, OverlappingFiles) { + Add("150", "600"); + Add("400", "500"); + disjoint_sorted_files_ = false; + ASSERT_TRUE(! Overlaps("100", "149")); + ASSERT_TRUE(! Overlaps("601", "700")); + ASSERT_TRUE(Overlaps("100", "150")); + ASSERT_TRUE(Overlaps("100", "200")); + ASSERT_TRUE(Overlaps("100", "300")); + ASSERT_TRUE(Overlaps("100", "400")); + ASSERT_TRUE(Overlaps("100", "500")); + ASSERT_TRUE(Overlaps("375", "400")); + ASSERT_TRUE(Overlaps("450", "450")); + ASSERT_TRUE(Overlaps("450", "500")); + ASSERT_TRUE(Overlaps("450", "700")); + ASSERT_TRUE(Overlaps("600", "700")); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/write_batch.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/write_batch.cc new file mode 100644 index 0000000..33f4a42 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/write_batch.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// WriteBatch::rep_ := +// sequence: fixed64 +// count: fixed32 +// data: record[count] +// record := +// kTypeValue varstring varstring | +// kTypeDeletion varstring +// varstring := +// len: varint32 +// data: uint8[len] + +#include "leveldb/write_batch.h" + +#include "leveldb/db.h" +#include "db/dbformat.h" +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "util/coding.h" + +namespace leveldb { + +// WriteBatch header has an 8-byte sequence number followed by a 4-byte count. +static const size_t kHeader = 12; + +WriteBatch::WriteBatch() { + Clear(); +} + +WriteBatch::~WriteBatch() { } + +WriteBatch::Handler::~Handler() { } + +void WriteBatch::Clear() { + rep_.clear(); + rep_.resize(kHeader); +} + +Status WriteBatch::Iterate(Handler* handler) const { + Slice input(rep_); + if (input.size() < kHeader) { + return Status::Corruption("malformed WriteBatch (too small)"); + } + + input.remove_prefix(kHeader); + Slice key, value; + int found = 0; + while (!input.empty()) { + found++; + char tag = input[0]; + input.remove_prefix(1); + switch (tag) { + case kTypeValue: + if (GetLengthPrefixedSlice(&input, &key) && + GetLengthPrefixedSlice(&input, &value)) { + handler->Put(key, value); + } else { + return Status::Corruption("bad WriteBatch Put"); + } + break; + case kTypeDeletion: + if (GetLengthPrefixedSlice(&input, &key)) { + handler->Delete(key); + } else { + return Status::Corruption("bad WriteBatch Delete"); + } + break; + default: + return Status::Corruption("unknown WriteBatch tag"); + } + } + if (found != WriteBatchInternal::Count(this)) { + return Status::Corruption("WriteBatch has wrong count"); + } else { + return Status::OK(); + } +} + +int WriteBatchInternal::Count(const WriteBatch* b) { + return DecodeFixed32(b->rep_.data() + 8); +} + +void WriteBatchInternal::SetCount(WriteBatch* b, int n) { + EncodeFixed32(&b->rep_[8], n); +} + +SequenceNumber WriteBatchInternal::Sequence(const WriteBatch* b) { + return SequenceNumber(DecodeFixed64(b->rep_.data())); +} + +void WriteBatchInternal::SetSequence(WriteBatch* b, SequenceNumber seq) { + EncodeFixed64(&b->rep_[0], seq); +} + +void WriteBatch::Put(const Slice& key, const Slice& value) { + WriteBatchInternal::SetCount(this, WriteBatchInternal::Count(this) + 1); + rep_.push_back(static_cast(kTypeValue)); + PutLengthPrefixedSlice(&rep_, key); + PutLengthPrefixedSlice(&rep_, value); +} + +void WriteBatch::Delete(const Slice& key) { + WriteBatchInternal::SetCount(this, WriteBatchInternal::Count(this) + 1); + rep_.push_back(static_cast(kTypeDeletion)); + PutLengthPrefixedSlice(&rep_, key); +} + +namespace { +class MemTableInserter : public WriteBatch::Handler { + public: + SequenceNumber sequence_; + MemTable* mem_; + + virtual void Put(const Slice& key, const Slice& value) { + mem_->Add(sequence_, kTypeValue, key, value); + sequence_++; + } + virtual void Delete(const Slice& key) { + mem_->Add(sequence_, kTypeDeletion, key, Slice()); + sequence_++; + } +}; +} // namespace + +Status WriteBatchInternal::InsertInto(const WriteBatch* b, + MemTable* memtable) { + MemTableInserter inserter; + inserter.sequence_ = WriteBatchInternal::Sequence(b); + inserter.mem_ = memtable; + return b->Iterate(&inserter); +} + +void WriteBatchInternal::SetContents(WriteBatch* b, const Slice& contents) { + assert(contents.size() >= kHeader); + b->rep_.assign(contents.data(), contents.size()); +} + +void WriteBatchInternal::Append(WriteBatch* dst, const WriteBatch* src) { + SetCount(dst, Count(dst) + Count(src)); + assert(src->rep_.size() >= kHeader); + dst->rep_.append(src->rep_.data() + kHeader, src->rep_.size() - kHeader); +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/write_batch_internal.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/write_batch_internal.h new file mode 100644 index 0000000..9448ef7 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/write_batch_internal.h @@ -0,0 +1,50 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ +#define STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ + +#include "db/dbformat.h" +#include "leveldb/write_batch.h" + +namespace leveldb { + +class MemTable; + +// WriteBatchInternal provides static methods for manipulating a +// WriteBatch that we don't want in the public WriteBatch interface. +class WriteBatchInternal { + public: + // Return the number of entries in the batch. + static int Count(const WriteBatch* batch); + + // Set the count for the number of entries in the batch. + static void SetCount(WriteBatch* batch, int n); + + // Return the sequence number for the start of this batch. + static SequenceNumber Sequence(const WriteBatch* batch); + + // Store the specified number as the sequence number for the start of + // this batch. + static void SetSequence(WriteBatch* batch, SequenceNumber seq); + + static Slice Contents(const WriteBatch* batch) { + return Slice(batch->rep_); + } + + static size_t ByteSize(const WriteBatch* batch) { + return batch->rep_.size(); + } + + static void SetContents(WriteBatch* batch, const Slice& contents); + + static Status InsertInto(const WriteBatch* batch, MemTable* memtable); + + static void Append(WriteBatch* dst, const WriteBatch* src); +}; + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/write_batch_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/write_batch_test.cc new file mode 100644 index 0000000..9064e3d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/write_batch_test.cc @@ -0,0 +1,120 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" + +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "leveldb/env.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +static std::string PrintContents(WriteBatch* b) { + InternalKeyComparator cmp(BytewiseComparator()); + MemTable* mem = new MemTable(cmp); + mem->Ref(); + std::string state; + Status s = WriteBatchInternal::InsertInto(b, mem); + int count = 0; + Iterator* iter = mem->NewIterator(); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ParsedInternalKey ikey; + ASSERT_TRUE(ParseInternalKey(iter->key(), &ikey)); + switch (ikey.type) { + case kTypeValue: + state.append("Put("); + state.append(ikey.user_key.ToString()); + state.append(", "); + state.append(iter->value().ToString()); + state.append(")"); + count++; + break; + case kTypeDeletion: + state.append("Delete("); + state.append(ikey.user_key.ToString()); + state.append(")"); + count++; + break; + } + state.append("@"); + state.append(NumberToString(ikey.sequence)); + } + delete iter; + if (!s.ok()) { + state.append("ParseError()"); + } else if (count != WriteBatchInternal::Count(b)) { + state.append("CountMismatch()"); + } + mem->Unref(); + return state; +} + +class WriteBatchTest { }; + +TEST(WriteBatchTest, Empty) { + WriteBatch batch; + ASSERT_EQ("", PrintContents(&batch)); + ASSERT_EQ(0, WriteBatchInternal::Count(&batch)); +} + +TEST(WriteBatchTest, Multiple) { + WriteBatch batch; + batch.Put(Slice("foo"), Slice("bar")); + batch.Delete(Slice("box")); + batch.Put(Slice("baz"), Slice("boo")); + WriteBatchInternal::SetSequence(&batch, 100); + ASSERT_EQ(100, WriteBatchInternal::Sequence(&batch)); + ASSERT_EQ(3, WriteBatchInternal::Count(&batch)); + ASSERT_EQ("Put(baz, boo)@102" + "Delete(box)@101" + "Put(foo, bar)@100", + PrintContents(&batch)); +} + +TEST(WriteBatchTest, Corruption) { + WriteBatch batch; + batch.Put(Slice("foo"), Slice("bar")); + batch.Delete(Slice("box")); + WriteBatchInternal::SetSequence(&batch, 200); + Slice contents = WriteBatchInternal::Contents(&batch); + WriteBatchInternal::SetContents(&batch, + Slice(contents.data(),contents.size()-1)); + ASSERT_EQ("Put(foo, bar)@200" + "ParseError()", + PrintContents(&batch)); +} + +TEST(WriteBatchTest, Append) { + WriteBatch b1, b2; + WriteBatchInternal::SetSequence(&b1, 200); + WriteBatchInternal::SetSequence(&b2, 300); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("", + PrintContents(&b1)); + b2.Put("a", "va"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200", + PrintContents(&b1)); + b2.Clear(); + b2.Put("b", "vb"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200" + "Put(b, vb)@201", + PrintContents(&b1)); + b2.Delete("foo"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200" + "Put(b, vb)@202" + "Put(b, vb)@201" + "Delete(foo)@203", + PrintContents(&b1)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/helpers/memenv/memenv.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/helpers/memenv/memenv.cc new file mode 100644 index 0000000..9a98884 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/helpers/memenv/memenv.cc @@ -0,0 +1,398 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "helpers/memenv/memenv.h" + +#include "leveldb/env.h" +#include "leveldb/status.h" +#include "port/port.h" +#include "util/mutexlock.h" +#include +#include +#include +#include + +namespace leveldb { + +namespace { + +class FileState { + public: + // FileStates are reference counted. The initial reference count is zero + // and the caller must call Ref() at least once. + FileState() : refs_(0), size_(0) {} + + // Increase the reference count. + void Ref() { + MutexLock lock(&refs_mutex_); + ++refs_; + } + + // Decrease the reference count. Delete if this is the last reference. + void Unref() { + bool do_delete = false; + + { + MutexLock lock(&refs_mutex_); + --refs_; + assert(refs_ >= 0); + if (refs_ <= 0) { + do_delete = true; + } + } + + if (do_delete) { + delete this; + } + } + + uint64_t Size() const { return size_; } + + Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const { + if (offset > size_) { + return Status::IOError("Offset greater than file size."); + } + const uint64_t available = size_ - offset; + if (n > available) { + n = static_cast(available); + } + if (n == 0) { + *result = Slice(); + return Status::OK(); + } + + assert(offset / kBlockSize <= SIZE_MAX); + size_t block = static_cast(offset / kBlockSize); + size_t block_offset = offset % kBlockSize; + + if (n <= kBlockSize - block_offset) { + // The requested bytes are all in the first block. + *result = Slice(blocks_[block] + block_offset, n); + return Status::OK(); + } + + size_t bytes_to_copy = n; + char* dst = scratch; + + while (bytes_to_copy > 0) { + size_t avail = kBlockSize - block_offset; + if (avail > bytes_to_copy) { + avail = bytes_to_copy; + } + memcpy(dst, blocks_[block] + block_offset, avail); + + bytes_to_copy -= avail; + dst += avail; + block++; + block_offset = 0; + } + + *result = Slice(scratch, n); + return Status::OK(); + } + + Status Append(const Slice& data) { + const char* src = data.data(); + size_t src_len = data.size(); + + while (src_len > 0) { + size_t avail; + size_t offset = size_ % kBlockSize; + + if (offset != 0) { + // There is some room in the last block. + avail = kBlockSize - offset; + } else { + // No room in the last block; push new one. + blocks_.push_back(new char[kBlockSize]); + avail = kBlockSize; + } + + if (avail > src_len) { + avail = src_len; + } + memcpy(blocks_.back() + offset, src, avail); + src_len -= avail; + src += avail; + size_ += avail; + } + + return Status::OK(); + } + + private: + // Private since only Unref() should be used to delete it. + ~FileState() { + for (std::vector::iterator i = blocks_.begin(); i != blocks_.end(); + ++i) { + delete [] *i; + } + } + + // No copying allowed. + FileState(const FileState&); + void operator=(const FileState&); + + port::Mutex refs_mutex_; + int refs_; // Protected by refs_mutex_; + + // The following fields are not protected by any mutex. They are only mutable + // while the file is being written, and concurrent access is not allowed + // to writable files. + std::vector blocks_; + uint64_t size_; + + enum { kBlockSize = 8 * 1024 }; +}; + +class SequentialFileImpl : public SequentialFile { + public: + explicit SequentialFileImpl(FileState* file) : file_(file), pos_(0) { + file_->Ref(); + } + + ~SequentialFileImpl() { + file_->Unref(); + } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + Status s = file_->Read(pos_, n, result, scratch); + if (s.ok()) { + pos_ += result->size(); + } + return s; + } + + virtual Status Skip(uint64_t n) { + if (pos_ > file_->Size()) { + return Status::IOError("pos_ > file_->Size()"); + } + const uint64_t available = file_->Size() - pos_; + if (n > available) { + n = available; + } + pos_ += n; + return Status::OK(); + } + + private: + FileState* file_; + uint64_t pos_; +}; + +class RandomAccessFileImpl : public RandomAccessFile { + public: + explicit RandomAccessFileImpl(FileState* file) : file_(file) { + file_->Ref(); + } + + ~RandomAccessFileImpl() { + file_->Unref(); + } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + return file_->Read(offset, n, result, scratch); + } + + private: + FileState* file_; +}; + +class WritableFileImpl : public WritableFile { + public: + WritableFileImpl(FileState* file) : file_(file) { + file_->Ref(); + } + + ~WritableFileImpl() { + file_->Unref(); + } + + virtual Status Append(const Slice& data) { + return file_->Append(data); + } + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + + private: + FileState* file_; +}; + +class NoOpLogger : public Logger { + public: + virtual void Logv(const char* format, va_list ap) { } +}; + +class InMemoryEnv : public EnvWrapper { + public: + explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) { } + + virtual ~InMemoryEnv() { + for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){ + i->second->Unref(); + } + } + + // Partial implementation of the Env interface. + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + *result = NULL; + return Status::IOError(fname, "File not found"); + } + + *result = new SequentialFileImpl(file_map_[fname]); + return Status::OK(); + } + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + *result = NULL; + return Status::IOError(fname, "File not found"); + } + + *result = new RandomAccessFileImpl(file_map_[fname]); + return Status::OK(); + } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) != file_map_.end()) { + DeleteFileInternal(fname); + } + + FileState* file = new FileState(); + file->Ref(); + file_map_[fname] = file; + + *result = new WritableFileImpl(file); + return Status::OK(); + } + + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result) { + MutexLock lock(&mutex_); + FileState** sptr = &file_map_[fname]; + FileState* file = *sptr; + if (file == NULL) { + file = new FileState(); + file->Ref(); + } + *result = new WritableFileImpl(file); + return Status::OK(); + } + + virtual bool FileExists(const std::string& fname) { + MutexLock lock(&mutex_); + return file_map_.find(fname) != file_map_.end(); + } + + virtual Status GetChildren(const std::string& dir, + std::vector* result) { + MutexLock lock(&mutex_); + result->clear(); + + for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){ + const std::string& filename = i->first; + + if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' && + Slice(filename).starts_with(Slice(dir))) { + result->push_back(filename.substr(dir.size() + 1)); + } + } + + return Status::OK(); + } + + void DeleteFileInternal(const std::string& fname) { + if (file_map_.find(fname) == file_map_.end()) { + return; + } + + file_map_[fname]->Unref(); + file_map_.erase(fname); + } + + virtual Status DeleteFile(const std::string& fname) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + return Status::IOError(fname, "File not found"); + } + + DeleteFileInternal(fname); + return Status::OK(); + } + + virtual Status CreateDir(const std::string& dirname) { + return Status::OK(); + } + + virtual Status DeleteDir(const std::string& dirname) { + return Status::OK(); + } + + virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + return Status::IOError(fname, "File not found"); + } + + *file_size = file_map_[fname]->Size(); + return Status::OK(); + } + + virtual Status RenameFile(const std::string& src, + const std::string& target) { + MutexLock lock(&mutex_); + if (file_map_.find(src) == file_map_.end()) { + return Status::IOError(src, "File not found"); + } + + DeleteFileInternal(target); + file_map_[target] = file_map_[src]; + file_map_.erase(src); + return Status::OK(); + } + + virtual Status LockFile(const std::string& fname, FileLock** lock) { + *lock = new FileLock; + return Status::OK(); + } + + virtual Status UnlockFile(FileLock* lock) { + delete lock; + return Status::OK(); + } + + virtual Status GetTestDirectory(std::string* path) { + *path = "/test"; + return Status::OK(); + } + + virtual Status NewLogger(const std::string& fname, Logger** result) { + *result = new NoOpLogger; + return Status::OK(); + } + + private: + // Map from filenames to FileState objects, representing a simple file system. + typedef std::map FileSystem; + port::Mutex mutex_; + FileSystem file_map_; // Protected by mutex_. +}; + +} // namespace + +Env* NewMemEnv(Env* base_env) { + return new InMemoryEnv(base_env); +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/helpers/memenv/memenv.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/helpers/memenv/memenv.h new file mode 100644 index 0000000..03b88de --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/helpers/memenv/memenv.h @@ -0,0 +1,20 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ +#define STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ + +namespace leveldb { + +class Env; + +// Returns a new environment that stores its data in memory and delegates +// all non-file-storage tasks to base_env. The caller must delete the result +// when it is no longer needed. +// *base_env must remain live while the result is in use. +Env* NewMemEnv(Env* base_env); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/helpers/memenv/memenv_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/helpers/memenv/memenv_test.cc new file mode 100644 index 0000000..5cff776 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/helpers/memenv/memenv_test.cc @@ -0,0 +1,241 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "helpers/memenv/memenv.h" + +#include "db/db_impl.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "util/testharness.h" +#include +#include + +namespace leveldb { + +class MemEnvTest { + public: + Env* env_; + + MemEnvTest() + : env_(NewMemEnv(Env::Default())) { + } + ~MemEnvTest() { + delete env_; + } +}; + +TEST(MemEnvTest, Basics) { + uint64_t file_size; + WritableFile* writable_file; + std::vector children; + + ASSERT_OK(env_->CreateDir("/dir")); + + // Check that the directory is empty. + ASSERT_TRUE(!env_->FileExists("/dir/non_existent")); + ASSERT_TRUE(!env_->GetFileSize("/dir/non_existent", &file_size).ok()); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(0, children.size()); + + // Create a file. + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(0, file_size); + delete writable_file; + + // Check that the file exists. + ASSERT_TRUE(env_->FileExists("/dir/f")); + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(0, file_size); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(1, children.size()); + ASSERT_EQ("f", children[0]); + + // Write to the file. + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("abc")); + delete writable_file; + + // Check that append works. + ASSERT_OK(env_->NewAppendableFile("/dir/f", &writable_file)); + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(3, file_size); + ASSERT_OK(writable_file->Append("hello")); + delete writable_file; + + // Check for expected size. + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(8, file_size); + + // Check that renaming works. + ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok()); + ASSERT_OK(env_->RenameFile("/dir/f", "/dir/g")); + ASSERT_TRUE(!env_->FileExists("/dir/f")); + ASSERT_TRUE(env_->FileExists("/dir/g")); + ASSERT_OK(env_->GetFileSize("/dir/g", &file_size)); + ASSERT_EQ(8, file_size); + + // Check that opening non-existent file fails. + SequentialFile* seq_file; + RandomAccessFile* rand_file; + ASSERT_TRUE(!env_->NewSequentialFile("/dir/non_existent", &seq_file).ok()); + ASSERT_TRUE(!seq_file); + ASSERT_TRUE(!env_->NewRandomAccessFile("/dir/non_existent", &rand_file).ok()); + ASSERT_TRUE(!rand_file); + + // Check that deleting works. + ASSERT_TRUE(!env_->DeleteFile("/dir/non_existent").ok()); + ASSERT_OK(env_->DeleteFile("/dir/g")); + ASSERT_TRUE(!env_->FileExists("/dir/g")); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(0, children.size()); + ASSERT_OK(env_->DeleteDir("/dir")); +} + +TEST(MemEnvTest, ReadWrite) { + WritableFile* writable_file; + SequentialFile* seq_file; + RandomAccessFile* rand_file; + Slice result; + char scratch[100]; + + ASSERT_OK(env_->CreateDir("/dir")); + + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("hello ")); + ASSERT_OK(writable_file->Append("world")); + delete writable_file; + + // Read sequentially. + ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); + ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello". + ASSERT_EQ(0, result.compare("hello")); + ASSERT_OK(seq_file->Skip(1)); + ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world". + ASSERT_EQ(0, result.compare("world")); + ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF. + ASSERT_EQ(0, result.size()); + ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file. + ASSERT_OK(seq_file->Read(1000, &result, scratch)); + ASSERT_EQ(0, result.size()); + delete seq_file; + + // Random reads. + ASSERT_OK(env_->NewRandomAccessFile("/dir/f", &rand_file)); + ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world". + ASSERT_EQ(0, result.compare("world")); + ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello". + ASSERT_EQ(0, result.compare("hello")); + ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d". + ASSERT_EQ(0, result.compare("d")); + + // Too high offset. + ASSERT_TRUE(!rand_file->Read(1000, 5, &result, scratch).ok()); + delete rand_file; +} + +TEST(MemEnvTest, Locks) { + FileLock* lock; + + // These are no-ops, but we test they return success. + ASSERT_OK(env_->LockFile("some file", &lock)); + ASSERT_OK(env_->UnlockFile(lock)); +} + +TEST(MemEnvTest, Misc) { + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + ASSERT_TRUE(!test_dir.empty()); + + WritableFile* writable_file; + ASSERT_OK(env_->NewWritableFile("/a/b", &writable_file)); + + // These are no-ops, but we test they return success. + ASSERT_OK(writable_file->Sync()); + ASSERT_OK(writable_file->Flush()); + ASSERT_OK(writable_file->Close()); + delete writable_file; +} + +TEST(MemEnvTest, LargeWrite) { + const size_t kWriteSize = 300 * 1024; + char* scratch = new char[kWriteSize * 2]; + + std::string write_data; + for (size_t i = 0; i < kWriteSize; ++i) { + write_data.append(1, static_cast(i)); + } + + WritableFile* writable_file; + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("foo")); + ASSERT_OK(writable_file->Append(write_data)); + delete writable_file; + + SequentialFile* seq_file; + Slice result; + ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); + ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo". + ASSERT_EQ(0, result.compare("foo")); + + size_t read = 0; + std::string read_data; + while (read < kWriteSize) { + ASSERT_OK(seq_file->Read(kWriteSize - read, &result, scratch)); + read_data.append(result.data(), result.size()); + read += result.size(); + } + ASSERT_TRUE(write_data == read_data); + delete seq_file; + delete [] scratch; +} + +TEST(MemEnvTest, DBTest) { + Options options; + options.create_if_missing = true; + options.env = env_; + DB* db; + + const Slice keys[] = {Slice("aaa"), Slice("bbb"), Slice("ccc")}; + const Slice vals[] = {Slice("foo"), Slice("bar"), Slice("baz")}; + + ASSERT_OK(DB::Open(options, "/dir/db", &db)); + for (size_t i = 0; i < 3; ++i) { + ASSERT_OK(db->Put(WriteOptions(), keys[i], vals[i])); + } + + for (size_t i = 0; i < 3; ++i) { + std::string res; + ASSERT_OK(db->Get(ReadOptions(), keys[i], &res)); + ASSERT_TRUE(res == vals[i]); + } + + Iterator* iterator = db->NewIterator(ReadOptions()); + iterator->SeekToFirst(); + for (size_t i = 0; i < 3; ++i) { + ASSERT_TRUE(iterator->Valid()); + ASSERT_TRUE(keys[i] == iterator->key()); + ASSERT_TRUE(vals[i] == iterator->value()); + iterator->Next(); + } + ASSERT_TRUE(!iterator->Valid()); + delete iterator; + + DBImpl* dbi = reinterpret_cast(db); + ASSERT_OK(dbi->TEST_CompactMemTable()); + + for (size_t i = 0; i < 3; ++i) { + std::string res; + ASSERT_OK(db->Get(ReadOptions(), keys[i], &res)); + ASSERT_TRUE(res == vals[i]); + } + + delete db; +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/c.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/c.h new file mode 100644 index 0000000..1048fe3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/c.h @@ -0,0 +1,290 @@ +/* Copyright (c) 2011 The LevelDB Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. See the AUTHORS file for names of contributors. + + C bindings for leveldb. May be useful as a stable ABI that can be + used by programs that keep leveldb in a shared library, or for + a JNI api. + + Does not support: + . getters for the option types + . custom comparators that implement key shortening + . custom iter, db, env, cache implementations using just the C bindings + + Some conventions: + + (1) We expose just opaque struct pointers and functions to clients. + This allows us to change internal representations without having to + recompile clients. + + (2) For simplicity, there is no equivalent to the Slice type. Instead, + the caller has to pass the pointer and length as separate + arguments. + + (3) Errors are represented by a null-terminated c string. NULL + means no error. All operations that can raise an error are passed + a "char** errptr" as the last argument. One of the following must + be true on entry: + *errptr == NULL + *errptr points to a malloc()ed null-terminated error message + (On Windows, *errptr must have been malloc()-ed by this library.) + On success, a leveldb routine leaves *errptr unchanged. + On failure, leveldb frees the old value of *errptr and + set *errptr to a malloc()ed error message. + + (4) Bools have the type unsigned char (0 == false; rest == true) + + (5) All of the pointer arguments must be non-NULL. +*/ + +#ifndef STORAGE_LEVELDB_INCLUDE_C_H_ +#define STORAGE_LEVELDB_INCLUDE_C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* Exported types */ + +typedef struct leveldb_t leveldb_t; +typedef struct leveldb_cache_t leveldb_cache_t; +typedef struct leveldb_comparator_t leveldb_comparator_t; +typedef struct leveldb_env_t leveldb_env_t; +typedef struct leveldb_filelock_t leveldb_filelock_t; +typedef struct leveldb_filterpolicy_t leveldb_filterpolicy_t; +typedef struct leveldb_iterator_t leveldb_iterator_t; +typedef struct leveldb_logger_t leveldb_logger_t; +typedef struct leveldb_options_t leveldb_options_t; +typedef struct leveldb_randomfile_t leveldb_randomfile_t; +typedef struct leveldb_readoptions_t leveldb_readoptions_t; +typedef struct leveldb_seqfile_t leveldb_seqfile_t; +typedef struct leveldb_snapshot_t leveldb_snapshot_t; +typedef struct leveldb_writablefile_t leveldb_writablefile_t; +typedef struct leveldb_writebatch_t leveldb_writebatch_t; +typedef struct leveldb_writeoptions_t leveldb_writeoptions_t; + +/* DB operations */ + +extern leveldb_t* leveldb_open( + const leveldb_options_t* options, + const char* name, + char** errptr); + +extern void leveldb_close(leveldb_t* db); + +extern void leveldb_put( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + const char* val, size_t vallen, + char** errptr); + +extern void leveldb_delete( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + char** errptr); + +extern void leveldb_write( + leveldb_t* db, + const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, + char** errptr); + +/* Returns NULL if not found. A malloc()ed array otherwise. + Stores the length of the array in *vallen. */ +extern char* leveldb_get( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, size_t keylen, + size_t* vallen, + char** errptr); + +extern leveldb_iterator_t* leveldb_create_iterator( + leveldb_t* db, + const leveldb_readoptions_t* options); + +extern const leveldb_snapshot_t* leveldb_create_snapshot( + leveldb_t* db); + +extern void leveldb_release_snapshot( + leveldb_t* db, + const leveldb_snapshot_t* snapshot); + +/* Returns NULL if property name is unknown. + Else returns a pointer to a malloc()-ed null-terminated value. */ +extern char* leveldb_property_value( + leveldb_t* db, + const char* propname); + +extern void leveldb_approximate_sizes( + leveldb_t* db, + int num_ranges, + const char* const* range_start_key, const size_t* range_start_key_len, + const char* const* range_limit_key, const size_t* range_limit_key_len, + uint64_t* sizes); + +extern void leveldb_compact_range( + leveldb_t* db, + const char* start_key, size_t start_key_len, + const char* limit_key, size_t limit_key_len); + +/* Management operations */ + +extern void leveldb_destroy_db( + const leveldb_options_t* options, + const char* name, + char** errptr); + +extern void leveldb_repair_db( + const leveldb_options_t* options, + const char* name, + char** errptr); + +/* Iterator */ + +extern void leveldb_iter_destroy(leveldb_iterator_t*); +extern unsigned char leveldb_iter_valid(const leveldb_iterator_t*); +extern void leveldb_iter_seek_to_first(leveldb_iterator_t*); +extern void leveldb_iter_seek_to_last(leveldb_iterator_t*); +extern void leveldb_iter_seek(leveldb_iterator_t*, const char* k, size_t klen); +extern void leveldb_iter_next(leveldb_iterator_t*); +extern void leveldb_iter_prev(leveldb_iterator_t*); +extern const char* leveldb_iter_key(const leveldb_iterator_t*, size_t* klen); +extern const char* leveldb_iter_value(const leveldb_iterator_t*, size_t* vlen); +extern void leveldb_iter_get_error(const leveldb_iterator_t*, char** errptr); + +/* Write batch */ + +extern leveldb_writebatch_t* leveldb_writebatch_create(); +extern void leveldb_writebatch_destroy(leveldb_writebatch_t*); +extern void leveldb_writebatch_clear(leveldb_writebatch_t*); +extern void leveldb_writebatch_put( + leveldb_writebatch_t*, + const char* key, size_t klen, + const char* val, size_t vlen); +extern void leveldb_writebatch_delete( + leveldb_writebatch_t*, + const char* key, size_t klen); +extern void leveldb_writebatch_iterate( + leveldb_writebatch_t*, + void* state, + void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), + void (*deleted)(void*, const char* k, size_t klen)); + +/* Options */ + +extern leveldb_options_t* leveldb_options_create(); +extern void leveldb_options_destroy(leveldb_options_t*); +extern void leveldb_options_set_comparator( + leveldb_options_t*, + leveldb_comparator_t*); +extern void leveldb_options_set_filter_policy( + leveldb_options_t*, + leveldb_filterpolicy_t*); +extern void leveldb_options_set_create_if_missing( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_error_if_exists( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_paranoid_checks( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_env(leveldb_options_t*, leveldb_env_t*); +extern void leveldb_options_set_info_log(leveldb_options_t*, leveldb_logger_t*); +extern void leveldb_options_set_write_buffer_size(leveldb_options_t*, size_t); +extern void leveldb_options_set_max_open_files(leveldb_options_t*, int); +extern void leveldb_options_set_cache(leveldb_options_t*, leveldb_cache_t*); +extern void leveldb_options_set_block_size(leveldb_options_t*, size_t); +extern void leveldb_options_set_block_restart_interval(leveldb_options_t*, int); + +enum { + leveldb_no_compression = 0, + leveldb_snappy_compression = 1 +}; +extern void leveldb_options_set_compression(leveldb_options_t*, int); + +/* Comparator */ + +extern leveldb_comparator_t* leveldb_comparator_create( + void* state, + void (*destructor)(void*), + int (*compare)( + void*, + const char* a, size_t alen, + const char* b, size_t blen), + const char* (*name)(void*)); +extern void leveldb_comparator_destroy(leveldb_comparator_t*); + +/* Filter policy */ + +extern leveldb_filterpolicy_t* leveldb_filterpolicy_create( + void* state, + void (*destructor)(void*), + char* (*create_filter)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length), + unsigned char (*key_may_match)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length), + const char* (*name)(void*)); +extern void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t*); + +extern leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom( + int bits_per_key); + +/* Read options */ + +extern leveldb_readoptions_t* leveldb_readoptions_create(); +extern void leveldb_readoptions_destroy(leveldb_readoptions_t*); +extern void leveldb_readoptions_set_verify_checksums( + leveldb_readoptions_t*, + unsigned char); +extern void leveldb_readoptions_set_fill_cache( + leveldb_readoptions_t*, unsigned char); +extern void leveldb_readoptions_set_snapshot( + leveldb_readoptions_t*, + const leveldb_snapshot_t*); + +/* Write options */ + +extern leveldb_writeoptions_t* leveldb_writeoptions_create(); +extern void leveldb_writeoptions_destroy(leveldb_writeoptions_t*); +extern void leveldb_writeoptions_set_sync( + leveldb_writeoptions_t*, unsigned char); + +/* Cache */ + +extern leveldb_cache_t* leveldb_cache_create_lru(size_t capacity); +extern void leveldb_cache_destroy(leveldb_cache_t* cache); + +/* Env */ + +extern leveldb_env_t* leveldb_create_default_env(); +extern void leveldb_env_destroy(leveldb_env_t*); + +/* Utility */ + +/* Calls free(ptr). + REQUIRES: ptr was malloc()-ed and returned by one of the routines + in this file. Note that in certain cases (typically on Windows), you + may need to call this routine instead of free(ptr) to dispose of + malloc()-ed memory returned by this library. */ +extern void leveldb_free(void* ptr); + +/* Return the major version number for this release. */ +extern int leveldb_major_version(); + +/* Return the minor version number for this release. */ +extern int leveldb_minor_version(); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* STORAGE_LEVELDB_INCLUDE_C_H_ */ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/cache.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/cache.h new file mode 100644 index 0000000..6819d5b --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/cache.h @@ -0,0 +1,110 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A Cache is an interface that maps keys to values. It has internal +// synchronization and may be safely accessed concurrently from +// multiple threads. It may automatically evict entries to make room +// for new entries. Values have a specified charge against the cache +// capacity. For example, a cache where the values are variable +// length strings, may use the length of the string as the charge for +// the string. +// +// A builtin cache implementation with a least-recently-used eviction +// policy is provided. Clients may use their own implementations if +// they want something more sophisticated (like scan-resistance, a +// custom eviction policy, variable cache sizing, etc.) + +#ifndef STORAGE_LEVELDB_INCLUDE_CACHE_H_ +#define STORAGE_LEVELDB_INCLUDE_CACHE_H_ + +#include +#include "leveldb/slice.h" + +namespace leveldb { + +class Cache; + +// Create a new cache with a fixed size capacity. This implementation +// of Cache uses a least-recently-used eviction policy. +extern Cache* NewLRUCache(size_t capacity); + +class Cache { + public: + Cache() { } + + // Destroys all existing entries by calling the "deleter" + // function that was passed to the constructor. + virtual ~Cache(); + + // Opaque handle to an entry stored in the cache. + struct Handle { }; + + // Insert a mapping from key->value into the cache and assign it + // the specified charge against the total cache capacity. + // + // Returns a handle that corresponds to the mapping. The caller + // must call this->Release(handle) when the returned mapping is no + // longer needed. + // + // When the inserted entry is no longer needed, the key and + // value will be passed to "deleter". + virtual Handle* Insert(const Slice& key, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) = 0; + + // If the cache has no mapping for "key", returns NULL. + // + // Else return a handle that corresponds to the mapping. The caller + // must call this->Release(handle) when the returned mapping is no + // longer needed. + virtual Handle* Lookup(const Slice& key) = 0; + + // Release a mapping returned by a previous Lookup(). + // REQUIRES: handle must not have been released yet. + // REQUIRES: handle must have been returned by a method on *this. + virtual void Release(Handle* handle) = 0; + + // Return the value encapsulated in a handle returned by a + // successful Lookup(). + // REQUIRES: handle must not have been released yet. + // REQUIRES: handle must have been returned by a method on *this. + virtual void* Value(Handle* handle) = 0; + + // If the cache contains entry for key, erase it. Note that the + // underlying entry will be kept around until all existing handles + // to it have been released. + virtual void Erase(const Slice& key) = 0; + + // Return a new numeric id. May be used by multiple clients who are + // sharing the same cache to partition the key space. Typically the + // client will allocate a new id at startup and prepend the id to + // its cache keys. + virtual uint64_t NewId() = 0; + + // Remove all cache entries that are not actively in use. Memory-constrained + // applications may wish to call this method to reduce memory usage. + // Default implementation of Prune() does nothing. Subclasses are strongly + // encouraged to override the default implementation. A future release of + // leveldb may change Prune() to a pure abstract method. + virtual void Prune() {} + + // Return an estimate of the combined charges of all elements stored in the + // cache. + virtual size_t TotalCharge() const = 0; + + private: + void LRU_Remove(Handle* e); + void LRU_Append(Handle* e); + void Unref(Handle* e); + + struct Rep; + Rep* rep_; + + // No copying allowed + Cache(const Cache&); + void operator=(const Cache&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_CACHE_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/comparator.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/comparator.h new file mode 100644 index 0000000..556b984 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/comparator.h @@ -0,0 +1,63 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ +#define STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ + +#include + +namespace leveldb { + +class Slice; + +// A Comparator object provides a total order across slices that are +// used as keys in an sstable or a database. A Comparator implementation +// must be thread-safe since leveldb may invoke its methods concurrently +// from multiple threads. +class Comparator { + public: + virtual ~Comparator(); + + // Three-way comparison. Returns value: + // < 0 iff "a" < "b", + // == 0 iff "a" == "b", + // > 0 iff "a" > "b" + virtual int Compare(const Slice& a, const Slice& b) const = 0; + + // The name of the comparator. Used to check for comparator + // mismatches (i.e., a DB created with one comparator is + // accessed using a different comparator. + // + // The client of this package should switch to a new name whenever + // the comparator implementation changes in a way that will cause + // the relative ordering of any two keys to change. + // + // Names starting with "leveldb." are reserved and should not be used + // by any clients of this package. + virtual const char* Name() const = 0; + + // Advanced functions: these are used to reduce the space requirements + // for internal data structures like index blocks. + + // If *start < limit, changes *start to a short string in [start,limit). + // Simple comparator implementations may return with *start unchanged, + // i.e., an implementation of this method that does nothing is correct. + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const = 0; + + // Changes *key to a short string >= *key. + // Simple comparator implementations may return with *key unchanged, + // i.e., an implementation of this method that does nothing is correct. + virtual void FindShortSuccessor(std::string* key) const = 0; +}; + +// Return a builtin comparator that uses lexicographic byte-wise +// ordering. The result remains the property of this module and +// must not be deleted. +extern const Comparator* BytewiseComparator(); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/db.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/db.h new file mode 100644 index 0000000..bfab10a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/db.h @@ -0,0 +1,163 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_DB_H_ +#define STORAGE_LEVELDB_INCLUDE_DB_H_ + +#include +#include +#include "leveldb/iterator.h" +#include "leveldb/options.h" + +namespace leveldb { + +// Update Makefile if you change these +static const int kMajorVersion = 1; +static const int kMinorVersion = 20; + +struct Options; +struct ReadOptions; +struct WriteOptions; +class WriteBatch; + +// Abstract handle to particular state of a DB. +// A Snapshot is an immutable object and can therefore be safely +// accessed from multiple threads without any external synchronization. +class Snapshot { + protected: + virtual ~Snapshot(); +}; + +// A range of keys +struct Range { + Slice start; // Included in the range + Slice limit; // Not included in the range + + Range() { } + Range(const Slice& s, const Slice& l) : start(s), limit(l) { } +}; + +// A DB is a persistent ordered map from keys to values. +// A DB is safe for concurrent access from multiple threads without +// any external synchronization. +class DB { + public: + // Open the database with the specified "name". + // Stores a pointer to a heap-allocated database in *dbptr and returns + // OK on success. + // Stores NULL in *dbptr and returns a non-OK status on error. + // Caller should delete *dbptr when it is no longer needed. + static Status Open(const Options& options, + const std::string& name, + DB** dbptr); + + DB() { } + virtual ~DB(); + + // Set the database entry for "key" to "value". Returns OK on success, + // and a non-OK status on error. + // Note: consider setting options.sync = true. + virtual Status Put(const WriteOptions& options, + const Slice& key, + const Slice& value) = 0; + + // Remove the database entry (if any) for "key". Returns OK on + // success, and a non-OK status on error. It is not an error if "key" + // did not exist in the database. + // Note: consider setting options.sync = true. + virtual Status Delete(const WriteOptions& options, const Slice& key) = 0; + + // Apply the specified updates to the database. + // Returns OK on success, non-OK on failure. + // Note: consider setting options.sync = true. + virtual Status Write(const WriteOptions& options, WriteBatch* updates) = 0; + + // If the database contains an entry for "key" store the + // corresponding value in *value and return OK. + // + // If there is no entry for "key" leave *value unchanged and return + // a status for which Status::IsNotFound() returns true. + // + // May return some other Status on an error. + virtual Status Get(const ReadOptions& options, + const Slice& key, std::string* value) = 0; + + // Return a heap-allocated iterator over the contents of the database. + // The result of NewIterator() is initially invalid (caller must + // call one of the Seek methods on the iterator before using it). + // + // Caller should delete the iterator when it is no longer needed. + // The returned iterator should be deleted before this db is deleted. + virtual Iterator* NewIterator(const ReadOptions& options) = 0; + + // Return a handle to the current DB state. Iterators created with + // this handle will all observe a stable snapshot of the current DB + // state. The caller must call ReleaseSnapshot(result) when the + // snapshot is no longer needed. + virtual const Snapshot* GetSnapshot() = 0; + + // Release a previously acquired snapshot. The caller must not + // use "snapshot" after this call. + virtual void ReleaseSnapshot(const Snapshot* snapshot) = 0; + + // DB implementations can export properties about their state + // via this method. If "property" is a valid property understood by this + // DB implementation, fills "*value" with its current value and returns + // true. Otherwise returns false. + // + // + // Valid property names include: + // + // "leveldb.num-files-at-level" - return the number of files at level , + // where is an ASCII representation of a level number (e.g. "0"). + // "leveldb.stats" - returns a multi-line string that describes statistics + // about the internal operation of the DB. + // "leveldb.sstables" - returns a multi-line string that describes all + // of the sstables that make up the db contents. + // "leveldb.approximate-memory-usage" - returns the approximate number of + // bytes of memory in use by the DB. + virtual bool GetProperty(const Slice& property, std::string* value) = 0; + + // For each i in [0,n-1], store in "sizes[i]", the approximate + // file system space used by keys in "[range[i].start .. range[i].limit)". + // + // Note that the returned sizes measure file system space usage, so + // if the user data compresses by a factor of ten, the returned + // sizes will be one-tenth the size of the corresponding user data size. + // + // The results may not include the sizes of recently written data. + virtual void GetApproximateSizes(const Range* range, int n, + uint64_t* sizes) = 0; + + // Compact the underlying storage for the key range [*begin,*end]. + // In particular, deleted and overwritten versions are discarded, + // and the data is rearranged to reduce the cost of operations + // needed to access the data. This operation should typically only + // be invoked by users who understand the underlying implementation. + // + // begin==NULL is treated as a key before all keys in the database. + // end==NULL is treated as a key after all keys in the database. + // Therefore the following call will compact the entire database: + // db->CompactRange(NULL, NULL); + virtual void CompactRange(const Slice* begin, const Slice* end) = 0; + + private: + // No copying allowed + DB(const DB&); + void operator=(const DB&); +}; + +// Destroy the contents of the specified database. +// Be very careful using this method. +Status DestroyDB(const std::string& name, const Options& options); + +// If a DB cannot be opened, you may attempt to call this method to +// resurrect as much of the contents of the database as possible. +// Some data may be lost, so be careful when calling this function +// on a database that contains important information. +Status RepairDB(const std::string& dbname, const Options& options); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_DB_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/dumpfile.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/dumpfile.h new file mode 100644 index 0000000..3f97fda --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/dumpfile.h @@ -0,0 +1,25 @@ +// Copyright (c) 2014 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_DUMPFILE_H_ +#define STORAGE_LEVELDB_INCLUDE_DUMPFILE_H_ + +#include +#include "leveldb/env.h" +#include "leveldb/status.h" + +namespace leveldb { + +// Dump the contents of the file named by fname in text format to +// *dst. Makes a sequence of dst->Append() calls; each call is passed +// the newline-terminated text corresponding to a single item found +// in the file. +// +// Returns a non-OK result if fname does not name a leveldb storage +// file, or if the file cannot be read. +Status DumpFile(Env* env, const std::string& fname, WritableFile* dst); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_DUMPFILE_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/env.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/env.h new file mode 100644 index 0000000..99b6c21 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/env.h @@ -0,0 +1,351 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// An Env is an interface used by the leveldb implementation to access +// operating system functionality like the filesystem etc. Callers +// may wish to provide a custom Env object when opening a database to +// get fine gain control; e.g., to rate limit file system operations. +// +// All Env implementations are safe for concurrent access from +// multiple threads without any external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_ENV_H_ +#define STORAGE_LEVELDB_INCLUDE_ENV_H_ + +#include +#include +#include +#include +#include "leveldb/status.h" + +namespace leveldb { + +class FileLock; +class Logger; +class RandomAccessFile; +class SequentialFile; +class Slice; +class WritableFile; + +class Env { + public: + Env() { } + virtual ~Env(); + + // Return a default environment suitable for the current operating + // system. Sophisticated users may wish to provide their own Env + // implementation instead of relying on this default environment. + // + // The result of Default() belongs to leveldb and must never be deleted. + static Env* Default(); + + // Create a brand new sequentially-readable file with the specified name. + // On success, stores a pointer to the new file in *result and returns OK. + // On failure stores NULL in *result and returns non-OK. If the file does + // not exist, returns a non-OK status. + // + // The returned file will only be accessed by one thread at a time. + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) = 0; + + // Create a brand new random access read-only file with the + // specified name. On success, stores a pointer to the new file in + // *result and returns OK. On failure stores NULL in *result and + // returns non-OK. If the file does not exist, returns a non-OK + // status. + // + // The returned file may be concurrently accessed by multiple threads. + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) = 0; + + // Create an object that writes to a new file with the specified + // name. Deletes any existing file with the same name and creates a + // new file. On success, stores a pointer to the new file in + // *result and returns OK. On failure stores NULL in *result and + // returns non-OK. + // + // The returned file will only be accessed by one thread at a time. + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) = 0; + + // Create an object that either appends to an existing file, or + // writes to a new file (if the file does not exist to begin with). + // On success, stores a pointer to the new file in *result and + // returns OK. On failure stores NULL in *result and returns + // non-OK. + // + // The returned file will only be accessed by one thread at a time. + // + // May return an IsNotSupportedError error if this Env does + // not allow appending to an existing file. Users of Env (including + // the leveldb implementation) must be prepared to deal with + // an Env that does not support appending. + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result); + + // Returns true iff the named file exists. + virtual bool FileExists(const std::string& fname) = 0; + + // Store in *result the names of the children of the specified directory. + // The names are relative to "dir". + // Original contents of *results are dropped. + virtual Status GetChildren(const std::string& dir, + std::vector* result) = 0; + + // Delete the named file. + virtual Status DeleteFile(const std::string& fname) = 0; + + // Create the specified directory. + virtual Status CreateDir(const std::string& dirname) = 0; + + // Delete the specified directory. + virtual Status DeleteDir(const std::string& dirname) = 0; + + // Store the size of fname in *file_size. + virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) = 0; + + // Rename file src to target. + virtual Status RenameFile(const std::string& src, + const std::string& target) = 0; + + // Lock the specified file. Used to prevent concurrent access to + // the same db by multiple processes. On failure, stores NULL in + // *lock and returns non-OK. + // + // On success, stores a pointer to the object that represents the + // acquired lock in *lock and returns OK. The caller should call + // UnlockFile(*lock) to release the lock. If the process exits, + // the lock will be automatically released. + // + // If somebody else already holds the lock, finishes immediately + // with a failure. I.e., this call does not wait for existing locks + // to go away. + // + // May create the named file if it does not already exist. + virtual Status LockFile(const std::string& fname, FileLock** lock) = 0; + + // Release the lock acquired by a previous successful call to LockFile. + // REQUIRES: lock was returned by a successful LockFile() call + // REQUIRES: lock has not already been unlocked. + virtual Status UnlockFile(FileLock* lock) = 0; + + // Arrange to run "(*function)(arg)" once in a background thread. + // + // "function" may run in an unspecified thread. Multiple functions + // added to the same Env may run concurrently in different threads. + // I.e., the caller may not assume that background work items are + // serialized. + virtual void Schedule( + void (*function)(void* arg), + void* arg) = 0; + + // Start a new thread, invoking "function(arg)" within the new thread. + // When "function(arg)" returns, the thread will be destroyed. + virtual void StartThread(void (*function)(void* arg), void* arg) = 0; + + // *path is set to a temporary directory that can be used for testing. It may + // or many not have just been created. The directory may or may not differ + // between runs of the same process, but subsequent calls will return the + // same directory. + virtual Status GetTestDirectory(std::string* path) = 0; + + // Create and return a log file for storing informational messages. + virtual Status NewLogger(const std::string& fname, Logger** result) = 0; + + // Returns the number of micro-seconds since some fixed point in time. Only + // useful for computing deltas of time. + virtual uint64_t NowMicros() = 0; + + // Sleep/delay the thread for the prescribed number of micro-seconds. + virtual void SleepForMicroseconds(int micros) = 0; + + private: + // No copying allowed + Env(const Env&); + void operator=(const Env&); +}; + +// A file abstraction for reading sequentially through a file +class SequentialFile { + public: + SequentialFile() { } + virtual ~SequentialFile(); + + // Read up to "n" bytes from the file. "scratch[0..n-1]" may be + // written by this routine. Sets "*result" to the data that was + // read (including if fewer than "n" bytes were successfully read). + // May set "*result" to point at data in "scratch[0..n-1]", so + // "scratch[0..n-1]" must be live when "*result" is used. + // If an error was encountered, returns a non-OK status. + // + // REQUIRES: External synchronization + virtual Status Read(size_t n, Slice* result, char* scratch) = 0; + + // Skip "n" bytes from the file. This is guaranteed to be no + // slower that reading the same data, but may be faster. + // + // If end of file is reached, skipping will stop at the end of the + // file, and Skip will return OK. + // + // REQUIRES: External synchronization + virtual Status Skip(uint64_t n) = 0; + + private: + // No copying allowed + SequentialFile(const SequentialFile&); + void operator=(const SequentialFile&); +}; + +// A file abstraction for randomly reading the contents of a file. +class RandomAccessFile { + public: + RandomAccessFile() { } + virtual ~RandomAccessFile(); + + // Read up to "n" bytes from the file starting at "offset". + // "scratch[0..n-1]" may be written by this routine. Sets "*result" + // to the data that was read (including if fewer than "n" bytes were + // successfully read). May set "*result" to point at data in + // "scratch[0..n-1]", so "scratch[0..n-1]" must be live when + // "*result" is used. If an error was encountered, returns a non-OK + // status. + // + // Safe for concurrent use by multiple threads. + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const = 0; + + private: + // No copying allowed + RandomAccessFile(const RandomAccessFile&); + void operator=(const RandomAccessFile&); +}; + +// A file abstraction for sequential writing. The implementation +// must provide buffering since callers may append small fragments +// at a time to the file. +class WritableFile { + public: + WritableFile() { } + virtual ~WritableFile(); + + virtual Status Append(const Slice& data) = 0; + virtual Status Close() = 0; + virtual Status Flush() = 0; + virtual Status Sync() = 0; + + private: + // No copying allowed + WritableFile(const WritableFile&); + void operator=(const WritableFile&); +}; + +// An interface for writing log messages. +class Logger { + public: + Logger() { } + virtual ~Logger(); + + // Write an entry to the log file with the specified format. + virtual void Logv(const char* format, va_list ap) = 0; + + private: + // No copying allowed + Logger(const Logger&); + void operator=(const Logger&); +}; + + +// Identifies a locked file. +class FileLock { + public: + FileLock() { } + virtual ~FileLock(); + private: + // No copying allowed + FileLock(const FileLock&); + void operator=(const FileLock&); +}; + +// Log the specified data to *info_log if info_log is non-NULL. +extern void Log(Logger* info_log, const char* format, ...) +# if defined(__GNUC__) || defined(__clang__) + __attribute__((__format__ (__printf__, 2, 3))) +# endif + ; + +// A utility routine: write "data" to the named file. +extern Status WriteStringToFile(Env* env, const Slice& data, + const std::string& fname); + +// A utility routine: read contents of named file into *data +extern Status ReadFileToString(Env* env, const std::string& fname, + std::string* data); + +// An implementation of Env that forwards all calls to another Env. +// May be useful to clients who wish to override just part of the +// functionality of another Env. +class EnvWrapper : public Env { + public: + // Initialize an EnvWrapper that delegates all calls to *t + explicit EnvWrapper(Env* t) : target_(t) { } + virtual ~EnvWrapper(); + + // Return the target to which this Env forwards all calls + Env* target() const { return target_; } + + // The following text is boilerplate that forwards all methods to target() + Status NewSequentialFile(const std::string& f, SequentialFile** r) { + return target_->NewSequentialFile(f, r); + } + Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { + return target_->NewRandomAccessFile(f, r); + } + Status NewWritableFile(const std::string& f, WritableFile** r) { + return target_->NewWritableFile(f, r); + } + Status NewAppendableFile(const std::string& f, WritableFile** r) { + return target_->NewAppendableFile(f, r); + } + bool FileExists(const std::string& f) { return target_->FileExists(f); } + Status GetChildren(const std::string& dir, std::vector* r) { + return target_->GetChildren(dir, r); + } + Status DeleteFile(const std::string& f) { return target_->DeleteFile(f); } + Status CreateDir(const std::string& d) { return target_->CreateDir(d); } + Status DeleteDir(const std::string& d) { return target_->DeleteDir(d); } + Status GetFileSize(const std::string& f, uint64_t* s) { + return target_->GetFileSize(f, s); + } + Status RenameFile(const std::string& s, const std::string& t) { + return target_->RenameFile(s, t); + } + Status LockFile(const std::string& f, FileLock** l) { + return target_->LockFile(f, l); + } + Status UnlockFile(FileLock* l) { return target_->UnlockFile(l); } + void Schedule(void (*f)(void*), void* a) { + return target_->Schedule(f, a); + } + void StartThread(void (*f)(void*), void* a) { + return target_->StartThread(f, a); + } + virtual Status GetTestDirectory(std::string* path) { + return target_->GetTestDirectory(path); + } + virtual Status NewLogger(const std::string& fname, Logger** result) { + return target_->NewLogger(fname, result); + } + uint64_t NowMicros() { + return target_->NowMicros(); + } + void SleepForMicroseconds(int micros) { + target_->SleepForMicroseconds(micros); + } + private: + Env* target_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_ENV_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/filter_policy.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/filter_policy.h new file mode 100644 index 0000000..1fba080 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/filter_policy.h @@ -0,0 +1,70 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A database can be configured with a custom FilterPolicy object. +// This object is responsible for creating a small filter from a set +// of keys. These filters are stored in leveldb and are consulted +// automatically by leveldb to decide whether or not to read some +// information from disk. In many cases, a filter can cut down the +// number of disk seeks form a handful to a single disk seek per +// DB::Get() call. +// +// Most people will want to use the builtin bloom filter support (see +// NewBloomFilterPolicy() below). + +#ifndef STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ +#define STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ + +#include + +namespace leveldb { + +class Slice; + +class FilterPolicy { + public: + virtual ~FilterPolicy(); + + // Return the name of this policy. Note that if the filter encoding + // changes in an incompatible way, the name returned by this method + // must be changed. Otherwise, old incompatible filters may be + // passed to methods of this type. + virtual const char* Name() const = 0; + + // keys[0,n-1] contains a list of keys (potentially with duplicates) + // that are ordered according to the user supplied comparator. + // Append a filter that summarizes keys[0,n-1] to *dst. + // + // Warning: do not change the initial contents of *dst. Instead, + // append the newly constructed filter to *dst. + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) + const = 0; + + // "filter" contains the data appended by a preceding call to + // CreateFilter() on this class. This method must return true if + // the key was in the list of keys passed to CreateFilter(). + // This method may return true or false if the key was not on the + // list, but it should aim to return false with a high probability. + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const = 0; +}; + +// Return a new filter policy that uses a bloom filter with approximately +// the specified number of bits per key. A good value for bits_per_key +// is 10, which yields a filter with ~ 1% false positive rate. +// +// Callers must delete the result after any database that is using the +// result has been closed. +// +// Note: if you are using a custom comparator that ignores some parts +// of the keys being compared, you must not use NewBloomFilterPolicy() +// and must provide your own FilterPolicy that also ignores the +// corresponding parts of the keys. For example, if the comparator +// ignores trailing spaces, it would be incorrect to use a +// FilterPolicy (like NewBloomFilterPolicy) that does not ignore +// trailing spaces in keys. +extern const FilterPolicy* NewBloomFilterPolicy(int bits_per_key); + +} + +#endif // STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/iterator.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/iterator.h new file mode 100644 index 0000000..da631ed --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/iterator.h @@ -0,0 +1,100 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// An iterator yields a sequence of key/value pairs from a source. +// The following class defines the interface. Multiple implementations +// are provided by this library. In particular, iterators are provided +// to access the contents of a Table or a DB. +// +// Multiple threads can invoke const methods on an Iterator without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Iterator must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ +#define STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ + +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class Iterator { + public: + Iterator(); + virtual ~Iterator(); + + // An iterator is either positioned at a key/value pair, or + // not valid. This method returns true iff the iterator is valid. + virtual bool Valid() const = 0; + + // Position at the first key in the source. The iterator is Valid() + // after this call iff the source is not empty. + virtual void SeekToFirst() = 0; + + // Position at the last key in the source. The iterator is + // Valid() after this call iff the source is not empty. + virtual void SeekToLast() = 0; + + // Position at the first key in the source that is at or past target. + // The iterator is Valid() after this call iff the source contains + // an entry that comes at or past target. + virtual void Seek(const Slice& target) = 0; + + // Moves to the next entry in the source. After this call, Valid() is + // true iff the iterator was not positioned at the last entry in the source. + // REQUIRES: Valid() + virtual void Next() = 0; + + // Moves to the previous entry in the source. After this call, Valid() is + // true iff the iterator was not positioned at the first entry in source. + // REQUIRES: Valid() + virtual void Prev() = 0; + + // Return the key for the current entry. The underlying storage for + // the returned slice is valid only until the next modification of + // the iterator. + // REQUIRES: Valid() + virtual Slice key() const = 0; + + // Return the value for the current entry. The underlying storage for + // the returned slice is valid only until the next modification of + // the iterator. + // REQUIRES: Valid() + virtual Slice value() const = 0; + + // If an error has occurred, return it. Else return an ok status. + virtual Status status() const = 0; + + // Clients are allowed to register function/arg1/arg2 triples that + // will be invoked when this iterator is destroyed. + // + // Note that unlike all of the preceding methods, this method is + // not abstract and therefore clients should not override it. + typedef void (*CleanupFunction)(void* arg1, void* arg2); + void RegisterCleanup(CleanupFunction function, void* arg1, void* arg2); + + private: + struct Cleanup { + CleanupFunction function; + void* arg1; + void* arg2; + Cleanup* next; + }; + Cleanup cleanup_; + + // No copying allowed + Iterator(const Iterator&); + void operator=(const Iterator&); +}; + +// Return an empty iterator (yields nothing). +extern Iterator* NewEmptyIterator(); + +// Return an empty iterator with the specified status. +extern Iterator* NewErrorIterator(const Status& status); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/options.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/options.h new file mode 100644 index 0000000..976e381 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/options.h @@ -0,0 +1,213 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ +#define STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ + +#include + +namespace leveldb { + +class Cache; +class Comparator; +class Env; +class FilterPolicy; +class Logger; +class Snapshot; + +// DB contents are stored in a set of blocks, each of which holds a +// sequence of key,value pairs. Each block may be compressed before +// being stored in a file. The following enum describes which +// compression method (if any) is used to compress a block. +enum CompressionType { + // NOTE: do not change the values of existing entries, as these are + // part of the persistent format on disk. + kNoCompression = 0x0, + kSnappyCompression = 0x1 +}; + +// Options to control the behavior of a database (passed to DB::Open) +struct Options { + // ------------------- + // Parameters that affect behavior + + // Comparator used to define the order of keys in the table. + // Default: a comparator that uses lexicographic byte-wise ordering + // + // REQUIRES: The client must ensure that the comparator supplied + // here has the same name and orders keys *exactly* the same as the + // comparator provided to previous open calls on the same DB. + const Comparator* comparator; + + // If true, the database will be created if it is missing. + // Default: false + bool create_if_missing; + + // If true, an error is raised if the database already exists. + // Default: false + bool error_if_exists; + + // If true, the implementation will do aggressive checking of the + // data it is processing and will stop early if it detects any + // errors. This may have unforeseen ramifications: for example, a + // corruption of one DB entry may cause a large number of entries to + // become unreadable or for the entire DB to become unopenable. + // Default: false + bool paranoid_checks; + + // Use the specified object to interact with the environment, + // e.g. to read/write files, schedule background work, etc. + // Default: Env::Default() + Env* env; + + // Any internal progress/error information generated by the db will + // be written to info_log if it is non-NULL, or to a file stored + // in the same directory as the DB contents if info_log is NULL. + // Default: NULL + Logger* info_log; + + // ------------------- + // Parameters that affect performance + + // Amount of data to build up in memory (backed by an unsorted log + // on disk) before converting to a sorted on-disk file. + // + // Larger values increase performance, especially during bulk loads. + // Up to two write buffers may be held in memory at the same time, + // so you may wish to adjust this parameter to control memory usage. + // Also, a larger write buffer will result in a longer recovery time + // the next time the database is opened. + // + // Default: 4MB + size_t write_buffer_size; + + // Number of open files that can be used by the DB. You may need to + // increase this if your database has a large working set (budget + // one open file per 2MB of working set). + // + // Default: 1000 + int max_open_files; + + // Control over blocks (user data is stored in a set of blocks, and + // a block is the unit of reading from disk). + + // If non-NULL, use the specified cache for blocks. + // If NULL, leveldb will automatically create and use an 8MB internal cache. + // Default: NULL + Cache* block_cache; + + // Approximate size of user data packed per block. Note that the + // block size specified here corresponds to uncompressed data. The + // actual size of the unit read from disk may be smaller if + // compression is enabled. This parameter can be changed dynamically. + // + // Default: 4K + size_t block_size; + + // Number of keys between restart points for delta encoding of keys. + // This parameter can be changed dynamically. Most clients should + // leave this parameter alone. + // + // Default: 16 + int block_restart_interval; + + // Leveldb will write up to this amount of bytes to a file before + // switching to a new one. + // Most clients should leave this parameter alone. However if your + // filesystem is more efficient with larger files, you could + // consider increasing the value. The downside will be longer + // compactions and hence longer latency/performance hiccups. + // Another reason to increase this parameter might be when you are + // initially populating a large database. + // + // Default: 2MB + size_t max_file_size; + + // Compress blocks using the specified compression algorithm. This + // parameter can be changed dynamically. + // + // Default: kSnappyCompression, which gives lightweight but fast + // compression. + // + // Typical speeds of kSnappyCompression on an Intel(R) Core(TM)2 2.4GHz: + // ~200-500MB/s compression + // ~400-800MB/s decompression + // Note that these speeds are significantly faster than most + // persistent storage speeds, and therefore it is typically never + // worth switching to kNoCompression. Even if the input data is + // incompressible, the kSnappyCompression implementation will + // efficiently detect that and will switch to uncompressed mode. + CompressionType compression; + + // EXPERIMENTAL: If true, append to existing MANIFEST and log files + // when a database is opened. This can significantly speed up open. + // + // Default: currently false, but may become true later. + bool reuse_logs; + + // If non-NULL, use the specified filter policy to reduce disk reads. + // Many applications will benefit from passing the result of + // NewBloomFilterPolicy() here. + // + // Default: NULL + const FilterPolicy* filter_policy; + + // Create an Options object with default values for all fields. + Options(); +}; + +// Options that control read operations +struct ReadOptions { + // If true, all data read from underlying storage will be + // verified against corresponding checksums. + // Default: false + bool verify_checksums; + + // Should the data read for this iteration be cached in memory? + // Callers may wish to set this field to false for bulk scans. + // Default: true + bool fill_cache; + + // If "snapshot" is non-NULL, read as of the supplied snapshot + // (which must belong to the DB that is being read and which must + // not have been released). If "snapshot" is NULL, use an implicit + // snapshot of the state at the beginning of this read operation. + // Default: NULL + const Snapshot* snapshot; + + ReadOptions() + : verify_checksums(false), + fill_cache(true), + snapshot(NULL) { + } +}; + +// Options that control write operations +struct WriteOptions { + // If true, the write will be flushed from the operating system + // buffer cache (by calling WritableFile::Sync()) before the write + // is considered complete. If this flag is true, writes will be + // slower. + // + // If this flag is false, and the machine crashes, some recent + // writes may be lost. Note that if it is just the process that + // crashes (i.e., the machine does not reboot), no writes will be + // lost even if sync==false. + // + // In other words, a DB write with sync==false has similar + // crash semantics as the "write()" system call. A DB write + // with sync==true has similar crash semantics to a "write()" + // system call followed by "fsync()". + // + // Default: false + bool sync; + + WriteOptions() + : sync(false) { + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/slice.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/slice.h new file mode 100644 index 0000000..bc36798 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/slice.h @@ -0,0 +1,109 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Slice is a simple structure containing a pointer into some external +// storage and a size. The user of a Slice must ensure that the slice +// is not used after the corresponding external storage has been +// deallocated. +// +// Multiple threads can invoke const methods on a Slice without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Slice must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_SLICE_H_ +#define STORAGE_LEVELDB_INCLUDE_SLICE_H_ + +#include +#include +#include +#include + +namespace leveldb { + +class Slice { + public: + // Create an empty slice. + Slice() : data_(""), size_(0) { } + + // Create a slice that refers to d[0,n-1]. + Slice(const char* d, size_t n) : data_(d), size_(n) { } + + // Create a slice that refers to the contents of "s" + Slice(const std::string& s) : data_(s.data()), size_(s.size()) { } + + // Create a slice that refers to s[0,strlen(s)-1] + Slice(const char* s) : data_(s), size_(strlen(s)) { } + + // Return a pointer to the beginning of the referenced data + const char* data() const { return data_; } + + // Return the length (in bytes) of the referenced data + size_t size() const { return size_; } + + // Return true iff the length of the referenced data is zero + bool empty() const { return size_ == 0; } + + // Return the ith byte in the referenced data. + // REQUIRES: n < size() + char operator[](size_t n) const { + assert(n < size()); + return data_[n]; + } + + // Change this slice to refer to an empty array + void clear() { data_ = ""; size_ = 0; } + + // Drop the first "n" bytes from this slice. + void remove_prefix(size_t n) { + assert(n <= size()); + data_ += n; + size_ -= n; + } + + // Return a string that contains the copy of the referenced data. + std::string ToString() const { return std::string(data_, size_); } + + // Three-way comparison. Returns value: + // < 0 iff "*this" < "b", + // == 0 iff "*this" == "b", + // > 0 iff "*this" > "b" + int compare(const Slice& b) const; + + // Return true iff "x" is a prefix of "*this" + bool starts_with(const Slice& x) const { + return ((size_ >= x.size_) && + (memcmp(data_, x.data_, x.size_) == 0)); + } + + private: + const char* data_; + size_t size_; + + // Intentionally copyable +}; + +inline bool operator==(const Slice& x, const Slice& y) { + return ((x.size() == y.size()) && + (memcmp(x.data(), y.data(), x.size()) == 0)); +} + +inline bool operator!=(const Slice& x, const Slice& y) { + return !(x == y); +} + +inline int Slice::compare(const Slice& b) const { + const size_t min_len = (size_ < b.size_) ? size_ : b.size_; + int r = memcmp(data_, b.data_, min_len); + if (r == 0) { + if (size_ < b.size_) r = -1; + else if (size_ > b.size_) r = +1; + } + return r; +} + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_INCLUDE_SLICE_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/status.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/status.h new file mode 100644 index 0000000..d9575f9 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/status.h @@ -0,0 +1,112 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A Status encapsulates the result of an operation. It may indicate success, +// or it may indicate an error with an associated error message. +// +// Multiple threads can invoke const methods on a Status without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Status must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_STATUS_H_ +#define STORAGE_LEVELDB_INCLUDE_STATUS_H_ + +#include +#include "leveldb/slice.h" + +namespace leveldb { + +class Status { + public: + // Create a success status. + Status() : state_(NULL) { } + ~Status() { delete[] state_; } + + // Copy the specified status. + Status(const Status& s); + void operator=(const Status& s); + + // Return a success status. + static Status OK() { return Status(); } + + // Return error status of an appropriate type. + static Status NotFound(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kNotFound, msg, msg2); + } + static Status Corruption(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kCorruption, msg, msg2); + } + static Status NotSupported(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kNotSupported, msg, msg2); + } + static Status InvalidArgument(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kInvalidArgument, msg, msg2); + } + static Status IOError(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kIOError, msg, msg2); + } + + // Returns true iff the status indicates success. + bool ok() const { return (state_ == NULL); } + + // Returns true iff the status indicates a NotFound error. + bool IsNotFound() const { return code() == kNotFound; } + + // Returns true iff the status indicates a Corruption error. + bool IsCorruption() const { return code() == kCorruption; } + + // Returns true iff the status indicates an IOError. + bool IsIOError() const { return code() == kIOError; } + + // Returns true iff the status indicates a NotSupportedError. + bool IsNotSupportedError() const { return code() == kNotSupported; } + + // Returns true iff the status indicates an InvalidArgument. + bool IsInvalidArgument() const { return code() == kInvalidArgument; } + + // Return a string representation of this status suitable for printing. + // Returns the string "OK" for success. + std::string ToString() const; + + private: + // OK status has a NULL state_. Otherwise, state_ is a new[] array + // of the following form: + // state_[0..3] == length of message + // state_[4] == code + // state_[5..] == message + const char* state_; + + enum Code { + kOk = 0, + kNotFound = 1, + kCorruption = 2, + kNotSupported = 3, + kInvalidArgument = 4, + kIOError = 5 + }; + + Code code() const { + return (state_ == NULL) ? kOk : static_cast(state_[4]); + } + + Status(Code code, const Slice& msg, const Slice& msg2); + static const char* CopyState(const char* s); +}; + +inline Status::Status(const Status& s) { + state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); +} +inline void Status::operator=(const Status& s) { + // The following condition catches both aliasing (when this == &s), + // and the common case where both s and *this are ok. + if (state_ != s.state_) { + delete[] state_; + state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); + } +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_STATUS_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/table.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/table.h new file mode 100644 index 0000000..a9746c3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/table.h @@ -0,0 +1,85 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_TABLE_H_ +#define STORAGE_LEVELDB_INCLUDE_TABLE_H_ + +#include +#include "leveldb/iterator.h" + +namespace leveldb { + +class Block; +class BlockHandle; +class Footer; +struct Options; +class RandomAccessFile; +struct ReadOptions; +class TableCache; + +// A Table is a sorted map from strings to strings. Tables are +// immutable and persistent. A Table may be safely accessed from +// multiple threads without external synchronization. +class Table { + public: + // Attempt to open the table that is stored in bytes [0..file_size) + // of "file", and read the metadata entries necessary to allow + // retrieving data from the table. + // + // If successful, returns ok and sets "*table" to the newly opened + // table. The client should delete "*table" when no longer needed. + // If there was an error while initializing the table, sets "*table" + // to NULL and returns a non-ok status. Does not take ownership of + // "*source", but the client must ensure that "source" remains live + // for the duration of the returned table's lifetime. + // + // *file must remain live while this Table is in use. + static Status Open(const Options& options, + RandomAccessFile* file, + uint64_t file_size, + Table** table); + + ~Table(); + + // Returns a new iterator over the table contents. + // The result of NewIterator() is initially invalid (caller must + // call one of the Seek methods on the iterator before using it). + Iterator* NewIterator(const ReadOptions&) const; + + // Given a key, return an approximate byte offset in the file where + // the data for that key begins (or would begin if the key were + // present in the file). The returned value is in terms of file + // bytes, and so includes effects like compression of the underlying data. + // E.g., the approximate offset of the last key in the table will + // be close to the file length. + uint64_t ApproximateOffsetOf(const Slice& key) const; + + private: + struct Rep; + Rep* rep_; + + explicit Table(Rep* rep) { rep_ = rep; } + static Iterator* BlockReader(void*, const ReadOptions&, const Slice&); + + // Calls (*handle_result)(arg, ...) with the entry found after a call + // to Seek(key). May not make such a call if filter policy says + // that key is not present. + friend class TableCache; + Status InternalGet( + const ReadOptions&, const Slice& key, + void* arg, + void (*handle_result)(void* arg, const Slice& k, const Slice& v)); + + + void ReadMeta(const Footer& footer); + void ReadFilter(const Slice& filter_handle_value); + + // No copying allowed + Table(const Table&); + void operator=(const Table&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_TABLE_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/table_builder.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/table_builder.h new file mode 100644 index 0000000..5fd1dc7 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/table_builder.h @@ -0,0 +1,92 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// TableBuilder provides the interface used to build a Table +// (an immutable and sorted map from keys to values). +// +// Multiple threads can invoke const methods on a TableBuilder without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same TableBuilder must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ +#define STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ + +#include +#include "leveldb/options.h" +#include "leveldb/status.h" + +namespace leveldb { + +class BlockBuilder; +class BlockHandle; +class WritableFile; + +class TableBuilder { + public: + // Create a builder that will store the contents of the table it is + // building in *file. Does not close the file. It is up to the + // caller to close the file after calling Finish(). + TableBuilder(const Options& options, WritableFile* file); + + // REQUIRES: Either Finish() or Abandon() has been called. + ~TableBuilder(); + + // Change the options used by this builder. Note: only some of the + // option fields can be changed after construction. If a field is + // not allowed to change dynamically and its value in the structure + // passed to the constructor is different from its value in the + // structure passed to this method, this method will return an error + // without changing any fields. + Status ChangeOptions(const Options& options); + + // Add key,value to the table being constructed. + // REQUIRES: key is after any previously added key according to comparator. + // REQUIRES: Finish(), Abandon() have not been called + void Add(const Slice& key, const Slice& value); + + // Advanced operation: flush any buffered key/value pairs to file. + // Can be used to ensure that two adjacent entries never live in + // the same data block. Most clients should not need to use this method. + // REQUIRES: Finish(), Abandon() have not been called + void Flush(); + + // Return non-ok iff some error has been detected. + Status status() const; + + // Finish building the table. Stops using the file passed to the + // constructor after this function returns. + // REQUIRES: Finish(), Abandon() have not been called + Status Finish(); + + // Indicate that the contents of this builder should be abandoned. Stops + // using the file passed to the constructor after this function returns. + // If the caller is not going to call Finish(), it must call Abandon() + // before destroying this builder. + // REQUIRES: Finish(), Abandon() have not been called + void Abandon(); + + // Number of calls to Add() so far. + uint64_t NumEntries() const; + + // Size of the file generated so far. If invoked after a successful + // Finish() call, returns the size of the final generated file. + uint64_t FileSize() const; + + private: + bool ok() const { return status().ok(); } + void WriteBlock(BlockBuilder* block, BlockHandle* handle); + void WriteRawBlock(const Slice& data, CompressionType, BlockHandle* handle); + + struct Rep; + Rep* rep_; + + // No copying allowed + TableBuilder(const TableBuilder&); + void operator=(const TableBuilder&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/write_batch.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/write_batch.h new file mode 100644 index 0000000..ee9aab6 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/write_batch.h @@ -0,0 +1,64 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// WriteBatch holds a collection of updates to apply atomically to a DB. +// +// The updates are applied in the order in which they are added +// to the WriteBatch. For example, the value of "key" will be "v3" +// after the following batch is written: +// +// batch.Put("key", "v1"); +// batch.Delete("key"); +// batch.Put("key", "v2"); +// batch.Put("key", "v3"); +// +// Multiple threads can invoke const methods on a WriteBatch without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same WriteBatch must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ +#define STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ + +#include +#include "leveldb/status.h" + +namespace leveldb { + +class Slice; + +class WriteBatch { + public: + WriteBatch(); + ~WriteBatch(); + + // Store the mapping "key->value" in the database. + void Put(const Slice& key, const Slice& value); + + // If the database contains a mapping for "key", erase it. Else do nothing. + void Delete(const Slice& key); + + // Clear all updates buffered in this batch. + void Clear(); + + // Support for iterating over the contents of a batch. + class Handler { + public: + virtual ~Handler(); + virtual void Put(const Slice& key, const Slice& value) = 0; + virtual void Delete(const Slice& key) = 0; + }; + Status Iterate(Handler* handler) const; + + private: + friend class WriteBatchInternal; + + std::string rep_; // See comment in write_batch.cc for the format of rep_ + + // Intentionally copyable +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/issues/issue178_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/issues/issue178_test.cc new file mode 100644 index 0000000..1b1cf8b --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/issues/issue178_test.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2013 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// Test for issue 178: a manual compaction causes deleted data to reappear. +#include +#include +#include + +#include "leveldb/db.h" +#include "leveldb/write_batch.h" +#include "util/testharness.h" + +namespace { + +const int kNumKeys = 1100000; + +std::string Key1(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "my_key_%d", i); + return buf; +} + +std::string Key2(int i) { + return Key1(i) + "_xxx"; +} + +class Issue178 { }; + +TEST(Issue178, Test) { + // Get rid of any state from an old run. + std::string dbpath = leveldb::test::TmpDir() + "/leveldb_cbug_test"; + DestroyDB(dbpath, leveldb::Options()); + + // Open database. Disable compression since it affects the creation + // of layers and the code below is trying to test against a very + // specific scenario. + leveldb::DB* db; + leveldb::Options db_options; + db_options.create_if_missing = true; + db_options.compression = leveldb::kNoCompression; + ASSERT_OK(leveldb::DB::Open(db_options, dbpath, &db)); + + // create first key range + leveldb::WriteBatch batch; + for (size_t i = 0; i < kNumKeys; i++) { + batch.Put(Key1(i), "value for range 1 key"); + } + ASSERT_OK(db->Write(leveldb::WriteOptions(), &batch)); + + // create second key range + batch.Clear(); + for (size_t i = 0; i < kNumKeys; i++) { + batch.Put(Key2(i), "value for range 2 key"); + } + ASSERT_OK(db->Write(leveldb::WriteOptions(), &batch)); + + // delete second key range + batch.Clear(); + for (size_t i = 0; i < kNumKeys; i++) { + batch.Delete(Key2(i)); + } + ASSERT_OK(db->Write(leveldb::WriteOptions(), &batch)); + + // compact database + std::string start_key = Key1(0); + std::string end_key = Key1(kNumKeys - 1); + leveldb::Slice least(start_key.data(), start_key.size()); + leveldb::Slice greatest(end_key.data(), end_key.size()); + + // commenting out the line below causes the example to work correctly + db->CompactRange(&least, &greatest); + + // count the keys + leveldb::Iterator* iter = db->NewIterator(leveldb::ReadOptions()); + size_t num_keys = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + num_keys++; + } + delete iter; + ASSERT_EQ(kNumKeys, num_keys) << "Bad number of keys"; + + // close database + delete db; + DestroyDB(dbpath, leveldb::Options()); +} + +} // anonymous namespace + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/issues/issue200_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/issues/issue200_test.cc new file mode 100644 index 0000000..1cec79f --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/issues/issue200_test.cc @@ -0,0 +1,59 @@ +// Copyright (c) 2013 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// Test for issue 200: when iterator switches direction from backward +// to forward, the current key can be yielded unexpectedly if a new +// mutation has been added just before the current key. + +#include "leveldb/db.h" +#include "util/testharness.h" + +namespace leveldb { + +class Issue200 { }; + +TEST(Issue200, Test) { + // Get rid of any state from an old run. + std::string dbpath = test::TmpDir() + "/leveldb_issue200_test"; + DestroyDB(dbpath, Options()); + + DB *db; + Options options; + options.create_if_missing = true; + ASSERT_OK(DB::Open(options, dbpath, &db)); + + WriteOptions write_options; + ASSERT_OK(db->Put(write_options, "1", "b")); + ASSERT_OK(db->Put(write_options, "2", "c")); + ASSERT_OK(db->Put(write_options, "3", "d")); + ASSERT_OK(db->Put(write_options, "4", "e")); + ASSERT_OK(db->Put(write_options, "5", "f")); + + ReadOptions read_options; + Iterator *iter = db->NewIterator(read_options); + + // Add an element that should not be reflected in the iterator. + ASSERT_OK(db->Put(write_options, "25", "cd")); + + iter->Seek("5"); + ASSERT_EQ(iter->key().ToString(), "5"); + iter->Prev(); + ASSERT_EQ(iter->key().ToString(), "4"); + iter->Prev(); + ASSERT_EQ(iter->key().ToString(), "3"); + iter->Next(); + ASSERT_EQ(iter->key().ToString(), "4"); + iter->Next(); + ASSERT_EQ(iter->key().ToString(), "5"); + + delete iter; + delete db; + DestroyDB(dbpath, options); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/README b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/README new file mode 100644 index 0000000..422563e --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/README @@ -0,0 +1,10 @@ +This directory contains interfaces and implementations that isolate the +rest of the package from platform details. + +Code in the rest of the package includes "port.h" from this directory. +"port.h" in turn includes a platform specific "port_.h" file +that provides the platform specific implementation. + +See port_posix.h for an example of what must be provided in a platform +specific header file. + diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/atomic_pointer.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/atomic_pointer.h new file mode 100644 index 0000000..1c4c7aa --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/atomic_pointer.h @@ -0,0 +1,242 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// AtomicPointer provides storage for a lock-free pointer. +// Platform-dependent implementation of AtomicPointer: +// - If the platform provides a cheap barrier, we use it with raw pointers +// - If is present (on newer versions of gcc, it is), we use +// a -based AtomicPointer. However we prefer the memory +// barrier based version, because at least on a gcc 4.4 32-bit build +// on linux, we have encountered a buggy implementation. +// Also, some implementations are much slower than a memory-barrier +// based implementation (~16ns for based acquire-load vs. ~1ns for +// a barrier based acquire-load). +// This code is based on atomicops-internals-* in Google's perftools: +// http://code.google.com/p/google-perftools/source/browse/#svn%2Ftrunk%2Fsrc%2Fbase + +#ifndef PORT_ATOMIC_POINTER_H_ +#define PORT_ATOMIC_POINTER_H_ + +#include +#ifdef LEVELDB_ATOMIC_PRESENT +#include +#endif +#ifdef OS_WIN +#include +#endif +#ifdef OS_MACOSX +#include +#endif + +#if defined(_M_X64) || defined(__x86_64__) +#define ARCH_CPU_X86_FAMILY 1 +#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) +#define ARCH_CPU_X86_FAMILY 1 +#elif defined(__ARMEL__) +#define ARCH_CPU_ARM_FAMILY 1 +#elif defined(__aarch64__) +#define ARCH_CPU_ARM64_FAMILY 1 +#elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__) +#define ARCH_CPU_PPC_FAMILY 1 +#elif defined(__mips__) +#define ARCH_CPU_MIPS_FAMILY 1 +#endif + +namespace leveldb { +namespace port { + +// Define MemoryBarrier() if available +// Windows on x86 +#if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY) +// windows.h already provides a MemoryBarrier(void) macro +// http://msdn.microsoft.com/en-us/library/ms684208(v=vs.85).aspx +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Mac OS +#elif defined(OS_MACOSX) +inline void MemoryBarrier() { + OSMemoryBarrier(); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Gcc on x86 +#elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__) +inline void MemoryBarrier() { + // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on + // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. + __asm__ __volatile__("" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Sun Studio +#elif defined(ARCH_CPU_X86_FAMILY) && defined(__SUNPRO_CC) +inline void MemoryBarrier() { + // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on + // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. + asm volatile("" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// ARM Linux +#elif defined(ARCH_CPU_ARM_FAMILY) && defined(__linux__) +typedef void (*LinuxKernelMemoryBarrierFunc)(void); +// The Linux ARM kernel provides a highly optimized device-specific memory +// barrier function at a fixed memory address that is mapped in every +// user-level process. +// +// This beats using CPU-specific instructions which are, on single-core +// devices, un-necessary and very costly (e.g. ARMv7-A "dmb" takes more +// than 180ns on a Cortex-A8 like the one on a Nexus One). Benchmarking +// shows that the extra function call cost is completely negligible on +// multi-core devices. +// +inline void MemoryBarrier() { + (*(LinuxKernelMemoryBarrierFunc)0xffff0fa0)(); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// ARM64 +#elif defined(ARCH_CPU_ARM64_FAMILY) +inline void MemoryBarrier() { + asm volatile("dmb sy" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// PPC +#elif defined(ARCH_CPU_PPC_FAMILY) && defined(__GNUC__) +inline void MemoryBarrier() { + // TODO for some powerpc expert: is there a cheaper suitable variant? + // Perhaps by having separate barriers for acquire and release ops. + asm volatile("sync" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// MIPS +#elif defined(ARCH_CPU_MIPS_FAMILY) && defined(__GNUC__) +inline void MemoryBarrier() { + __asm__ __volatile__("sync" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +#endif + +// AtomicPointer built using platform-specific MemoryBarrier() +#if defined(LEVELDB_HAVE_MEMORY_BARRIER) +class AtomicPointer { + private: + void* rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* p) : rep_(p) {} + inline void* NoBarrier_Load() const { return rep_; } + inline void NoBarrier_Store(void* v) { rep_ = v; } + inline void* Acquire_Load() const { + void* result = rep_; + MemoryBarrier(); + return result; + } + inline void Release_Store(void* v) { + MemoryBarrier(); + rep_ = v; + } +}; + +// AtomicPointer based on +#elif defined(LEVELDB_ATOMIC_PRESENT) +class AtomicPointer { + private: + std::atomic rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* v) : rep_(v) { } + inline void* Acquire_Load() const { + return rep_.load(std::memory_order_acquire); + } + inline void Release_Store(void* v) { + rep_.store(v, std::memory_order_release); + } + inline void* NoBarrier_Load() const { + return rep_.load(std::memory_order_relaxed); + } + inline void NoBarrier_Store(void* v) { + rep_.store(v, std::memory_order_relaxed); + } +}; + +// Atomic pointer based on sparc memory barriers +#elif defined(__sparcv9) && defined(__GNUC__) +class AtomicPointer { + private: + void* rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* v) : rep_(v) { } + inline void* Acquire_Load() const { + void* val; + __asm__ __volatile__ ( + "ldx [%[rep_]], %[val] \n\t" + "membar #LoadLoad|#LoadStore \n\t" + : [val] "=r" (val) + : [rep_] "r" (&rep_) + : "memory"); + return val; + } + inline void Release_Store(void* v) { + __asm__ __volatile__ ( + "membar #LoadStore|#StoreStore \n\t" + "stx %[v], [%[rep_]] \n\t" + : + : [rep_] "r" (&rep_), [v] "r" (v) + : "memory"); + } + inline void* NoBarrier_Load() const { return rep_; } + inline void NoBarrier_Store(void* v) { rep_ = v; } +}; + +// Atomic pointer based on ia64 acq/rel +#elif defined(__ia64) && defined(__GNUC__) +class AtomicPointer { + private: + void* rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* v) : rep_(v) { } + inline void* Acquire_Load() const { + void* val ; + __asm__ __volatile__ ( + "ld8.acq %[val] = [%[rep_]] \n\t" + : [val] "=r" (val) + : [rep_] "r" (&rep_) + : "memory" + ); + return val; + } + inline void Release_Store(void* v) { + __asm__ __volatile__ ( + "st8.rel [%[rep_]] = %[v] \n\t" + : + : [rep_] "r" (&rep_), [v] "r" (v) + : "memory" + ); + } + inline void* NoBarrier_Load() const { return rep_; } + inline void NoBarrier_Store(void* v) { rep_ = v; } +}; + +// We have neither MemoryBarrier(), nor +#else +#error Please implement AtomicPointer for this platform. + +#endif + +#undef LEVELDB_HAVE_MEMORY_BARRIER +#undef ARCH_CPU_X86_FAMILY +#undef ARCH_CPU_ARM_FAMILY +#undef ARCH_CPU_ARM64_FAMILY +#undef ARCH_CPU_PPC_FAMILY + +} // namespace port +} // namespace leveldb + +#endif // PORT_ATOMIC_POINTER_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port.h new file mode 100644 index 0000000..e009fb6 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port.h @@ -0,0 +1,21 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_PORT_PORT_H_ +#define STORAGE_LEVELDB_PORT_PORT_H_ + +#include + +// Include the appropriate platform specific file below. If you are +// porting to a new platform, see "port_example.h" for documentation +// of what the new port_.h file must provide. +#if defined(LEVELDB_PLATFORM_UV) +# include "port_uv.h" +#elif defined(LEVELDB_PLATFORM_POSIX) +# include "port/port_posix.h" +#elif defined(LEVELDB_PLATFORM_CHROMIUM) +# include "port/port_chromium.h" +#endif + +#endif // STORAGE_LEVELDB_PORT_PORT_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_example.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_example.h new file mode 100644 index 0000000..97bd669 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_example.h @@ -0,0 +1,141 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// This file contains the specification, but not the implementations, +// of the types/operations/etc. that should be defined by a platform +// specific port_.h file. Use this file as a reference for +// how to port this package to a new platform. + +#ifndef STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ +#define STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ + +namespace leveldb { +namespace port { + +// TODO(jorlow): Many of these belong more in the environment class rather than +// here. We should try moving them and see if it affects perf. + +// The following boolean constant must be true on a little-endian machine +// and false otherwise. +static const bool kLittleEndian = true /* or some other expression */; + +// ------------------ Threading ------------------- + +// A Mutex represents an exclusive lock. +class Mutex { + public: + Mutex(); + ~Mutex(); + + // Lock the mutex. Waits until other lockers have exited. + // Will deadlock if the mutex is already locked by this thread. + void Lock(); + + // Unlock the mutex. + // REQUIRES: This mutex was locked by this thread. + void Unlock(); + + // Optionally crash if this thread does not hold this mutex. + // The implementation must be fast, especially if NDEBUG is + // defined. The implementation is allowed to skip all checks. + void AssertHeld(); +}; + +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + + // Atomically release *mu and block on this condition variable until + // either a call to SignalAll(), or a call to Signal() that picks + // this thread to wakeup. + // REQUIRES: this thread holds *mu + void Wait(); + + // If there are some threads waiting, wake up at least one of them. + void Signal(); + + // Wake up all waiting threads. + void SignallAll(); +}; + +// Thread-safe initialization. +// Used as follows: +// static port::OnceType init_control = LEVELDB_ONCE_INIT; +// static void Initializer() { ... do something ...; } +// ... +// port::InitOnce(&init_control, &Initializer); +typedef intptr_t OnceType; +#define LEVELDB_ONCE_INIT 0 +extern void InitOnce(port::OnceType*, void (*initializer)()); + +// A type that holds a pointer that can be read or written atomically +// (i.e., without word-tearing.) +class AtomicPointer { + private: + intptr_t rep_; + public: + // Initialize to arbitrary value + AtomicPointer(); + + // Initialize to hold v + explicit AtomicPointer(void* v) : rep_(v) { } + + // Read and return the stored pointer with the guarantee that no + // later memory access (read or write) by this thread can be + // reordered ahead of this read. + void* Acquire_Load() const; + + // Set v as the stored pointer with the guarantee that no earlier + // memory access (read or write) by this thread can be reordered + // after this store. + void Release_Store(void* v); + + // Read the stored pointer with no ordering guarantees. + void* NoBarrier_Load() const; + + // Set va as the stored pointer with no ordering guarantees. + void NoBarrier_Store(void* v); +}; + +// ------------------ Compression ------------------- + +// Store the snappy compression of "input[0,input_length-1]" in *output. +// Returns false if snappy is not supported by this port. +extern bool Snappy_Compress(const char* input, size_t input_length, + std::string* output); + +// If input[0,input_length-1] looks like a valid snappy compressed +// buffer, store the size of the uncompressed data in *result and +// return true. Else return false. +extern bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result); + +// Attempt to snappy uncompress input[0,input_length-1] into *output. +// Returns true if successful, false if the input is invalid lightweight +// compressed data. +// +// REQUIRES: at least the first "n" bytes of output[] must be writable +// where "n" is the result of a successful call to +// Snappy_GetUncompressedLength. +extern bool Snappy_Uncompress(const char* input_data, size_t input_length, + char* output); + +// ------------------ Miscellaneous ------------------- + +// If heap profiling is not supported, returns false. +// Else repeatedly calls (*func)(arg, data, n) and then returns true. +// The concatenation of all "data[0,n-1]" fragments is the heap profile. +extern bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg); + +// Extend the CRC to include the first n bytes of buf. +// +// Returns zero if the CRC cannot be extended using acceleration, else returns +// the newly extended CRC value (which may also be zero). +uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size); + +} // namespace port +} // namespace leveldb + +#endif // STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_posix.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_posix.cc new file mode 100644 index 0000000..30e8007 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_posix.cc @@ -0,0 +1,53 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "port/port_posix.h" + +#include +#include +#include + +namespace leveldb { +namespace port { + +static void PthreadCall(const char* label, int result) { + if (result != 0) { + fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); + abort(); + } +} + +Mutex::Mutex() { PthreadCall("init mutex", pthread_mutex_init(&mu_, NULL)); } + +Mutex::~Mutex() { PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); } + +void Mutex::Lock() { PthreadCall("lock", pthread_mutex_lock(&mu_)); } + +void Mutex::Unlock() { PthreadCall("unlock", pthread_mutex_unlock(&mu_)); } + +CondVar::CondVar(Mutex* mu) + : mu_(mu) { + PthreadCall("init cv", pthread_cond_init(&cv_, NULL)); +} + +CondVar::~CondVar() { PthreadCall("destroy cv", pthread_cond_destroy(&cv_)); } + +void CondVar::Wait() { + PthreadCall("wait", pthread_cond_wait(&cv_, &mu_->mu_)); +} + +void CondVar::Signal() { + PthreadCall("signal", pthread_cond_signal(&cv_)); +} + +void CondVar::SignalAll() { + PthreadCall("broadcast", pthread_cond_broadcast(&cv_)); +} + +void InitOnce(OnceType* once, void (*initializer)()) { + PthreadCall("once", pthread_once(once, initializer)); +} + +} // namespace port +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_posix.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_posix.h new file mode 100644 index 0000000..b630b7b --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_posix.h @@ -0,0 +1,156 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +#ifndef STORAGE_LEVELDB_PORT_PORT_POSIX_H_ +#define STORAGE_LEVELDB_PORT_PORT_POSIX_H_ + +#undef PLATFORM_IS_LITTLE_ENDIAN +#if defined(OS_MACOSX) || defined(OS_IOS) + #include + #if defined(__DARWIN_LITTLE_ENDIAN) && defined(__DARWIN_BYTE_ORDER) + #define PLATFORM_IS_LITTLE_ENDIAN \ + (__DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN) + #endif +#elif defined(OS_SOLARIS) + #include + #ifdef _LITTLE_ENDIAN + #define PLATFORM_IS_LITTLE_ENDIAN true + #else + #define PLATFORM_IS_LITTLE_ENDIAN false + #endif +#elif defined(OS_FREEBSD) || defined(OS_OPENBSD) ||\ + defined(OS_NETBSD) || defined(OS_DRAGONFLYBSD) + #include + #include + #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) +#elif defined(OS_HPUX) + #define PLATFORM_IS_LITTLE_ENDIAN false +#elif defined(OS_ANDROID) + // Due to a bug in the NDK x86 definition, + // _BYTE_ORDER must be used instead of __BYTE_ORDER on Android. + // See http://code.google.com/p/android/issues/detail?id=39824 + #include + #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) +#else + #include +#endif + +#include +#ifdef SNAPPY +#include +#endif +#include +#include +#include "port/atomic_pointer.h" + +#ifndef PLATFORM_IS_LITTLE_ENDIAN +#define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) +#endif + +#if defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_SOLARIS) || defined(OS_FREEBSD) ||\ + defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) ||\ + defined(OS_ANDROID) || defined(OS_HPUX) || defined(CYGWIN) +// Use fread/fwrite/fflush on platforms without _unlocked variants +#define fread_unlocked fread +#define fwrite_unlocked fwrite +#define fflush_unlocked fflush +#endif + +#if defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_FREEBSD) ||\ + defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) +// Use fsync() on platforms without fdatasync() +#define fdatasync fsync +#endif + +#if defined(OS_ANDROID) && __ANDROID_API__ < 9 +// fdatasync() was only introduced in API level 9 on Android. Use fsync() +// when targetting older platforms. +#define fdatasync fsync +#endif + +namespace leveldb { +namespace port { + +static const bool kLittleEndian = PLATFORM_IS_LITTLE_ENDIAN; +#undef PLATFORM_IS_LITTLE_ENDIAN + +class CondVar; + +class Mutex { + public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); + void AssertHeld() { } + + private: + friend class CondVar; + pthread_mutex_t mu_; + + // No copying + Mutex(const Mutex&); + void operator=(const Mutex&); +}; + +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + void Wait(); + void Signal(); + void SignalAll(); + private: + pthread_cond_t cv_; + Mutex* mu_; +}; + +typedef pthread_once_t OnceType; +#define LEVELDB_ONCE_INIT PTHREAD_ONCE_INIT +extern void InitOnce(OnceType* once, void (*initializer)()); + +inline bool Snappy_Compress(const char* input, size_t length, + ::std::string* output) { +#ifdef SNAPPY + output->resize(snappy::MaxCompressedLength(length)); + size_t outlen; + snappy::RawCompress(input, length, &(*output)[0], &outlen); + output->resize(outlen); + return true; +#endif + + return false; +} + +inline bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result) { +#ifdef SNAPPY + return snappy::GetUncompressedLength(input, length, result); +#else + return false; +#endif +} + +inline bool Snappy_Uncompress(const char* input, size_t length, + char* output) { +#ifdef SNAPPY + return snappy::RawUncompress(input, length, output); +#else + return false; +#endif +} + +inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { + return false; +} + +uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size); + +} // namespace port +} // namespace leveldb + +#endif // STORAGE_LEVELDB_PORT_PORT_POSIX_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_posix_sse.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_posix_sse.cc new file mode 100644 index 0000000..1e519ba --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_posix_sse.cc @@ -0,0 +1,129 @@ +// Copyright 2016 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A portable implementation of crc32c, optimized to handle +// four bytes at a time. +// +// In a separate source file to allow this accelerated CRC32C function to be +// compiled with the appropriate compiler flags to enable x86 SSE 4.2 +// instructions. + +#include +#include +#include "port/port.h" + +#if defined(LEVELDB_PLATFORM_POSIX_SSE) + +#if defined(_MSC_VER) +#include +#elif defined(__GNUC__) && defined(__SSE4_2__) +#include +#include +#endif + +#endif // defined(LEVELDB_PLATFORM_POSIX_SSE) + +namespace leveldb { +namespace port { + +#if defined(LEVELDB_PLATFORM_POSIX_SSE) + +// Used to fetch a naturally-aligned 32-bit word in little endian byte-order +static inline uint32_t LE_LOAD32(const uint8_t *p) { + // SSE is x86 only, so ensured that |p| is always little-endian. + uint32_t word; + memcpy(&word, p, sizeof(word)); + return word; +} + +#if defined(_M_X64) || defined(__x86_64__) // LE_LOAD64 is only used on x64. + +// Used to fetch a naturally-aligned 64-bit word in little endian byte-order +static inline uint64_t LE_LOAD64(const uint8_t *p) { + uint64_t dword; + memcpy(&dword, p, sizeof(dword)); + return dword; +} + +#endif // defined(_M_X64) || defined(__x86_64__) + +static inline bool HaveSSE42() { +#if defined(_MSC_VER) + int cpu_info[4]; + __cpuid(cpu_info, 1); + return (cpu_info[2] & (1 << 20)) != 0; +#elif defined(__GNUC__) + unsigned int eax, ebx, ecx, edx; + __get_cpuid(1, &eax, &ebx, &ecx, &edx); + return (ecx & (1 << 20)) != 0; +#else + return false; +#endif +} + +#endif // defined(LEVELDB_PLATFORM_POSIX_SSE) + +// For further improvements see Intel publication at: +// http://download.intel.com/design/intarch/papers/323405.pdf +uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size) { +#if !defined(LEVELDB_PLATFORM_POSIX_SSE) + return 0; +#else + static bool have = HaveSSE42(); + if (!have) { + return 0; + } + + const uint8_t *p = reinterpret_cast(buf); + const uint8_t *e = p + size; + uint32_t l = crc ^ 0xffffffffu; + +#define STEP1 do { \ + l = _mm_crc32_u8(l, *p++); \ +} while (0) +#define STEP4 do { \ + l = _mm_crc32_u32(l, LE_LOAD32(p)); \ + p += 4; \ +} while (0) +#define STEP8 do { \ + l = _mm_crc32_u64(l, LE_LOAD64(p)); \ + p += 8; \ +} while (0) + + if (size > 16) { + // Process unaligned bytes + for (unsigned int i = reinterpret_cast(p) % 8; i; --i) { + STEP1; + } + + // _mm_crc32_u64 is only available on x64. +#if defined(_M_X64) || defined(__x86_64__) + // Process 8 bytes at a time + while ((e-p) >= 8) { + STEP8; + } + // Process 4 bytes at a time + if ((e-p) >= 4) { + STEP4; + } +#else // !(defined(_M_X64) || defined(__x86_64__)) + // Process 4 bytes at a time + while ((e-p) >= 4) { + STEP4; + } +#endif // defined(_M_X64) || defined(__x86_64__) + } + // Process the last few bytes + while (p != e) { + STEP1; + } +#undef STEP8 +#undef STEP4 +#undef STEP1 + return l ^ 0xffffffffu; +#endif // defined(LEVELDB_PLATFORM_POSIX_SSE) +} + +} // namespace port +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/thread_annotations.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/thread_annotations.h new file mode 100644 index 0000000..9470ef5 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/thread_annotations.h @@ -0,0 +1,60 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ +#define STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ + +// Some environments provide custom macros to aid in static thread-safety +// analysis. Provide empty definitions of such macros unless they are already +// defined. + +#ifndef EXCLUSIVE_LOCKS_REQUIRED +#define EXCLUSIVE_LOCKS_REQUIRED(...) +#endif + +#ifndef SHARED_LOCKS_REQUIRED +#define SHARED_LOCKS_REQUIRED(...) +#endif + +#ifndef LOCKS_EXCLUDED +#define LOCKS_EXCLUDED(...) +#endif + +#ifndef LOCK_RETURNED +#define LOCK_RETURNED(x) +#endif + +#ifndef LOCKABLE +#define LOCKABLE +#endif + +#ifndef SCOPED_LOCKABLE +#define SCOPED_LOCKABLE +#endif + +#ifndef EXCLUSIVE_LOCK_FUNCTION +#define EXCLUSIVE_LOCK_FUNCTION(...) +#endif + +#ifndef SHARED_LOCK_FUNCTION +#define SHARED_LOCK_FUNCTION(...) +#endif + +#ifndef EXCLUSIVE_TRYLOCK_FUNCTION +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) +#endif + +#ifndef SHARED_TRYLOCK_FUNCTION +#define SHARED_TRYLOCK_FUNCTION(...) +#endif + +#ifndef UNLOCK_FUNCTION +#define UNLOCK_FUNCTION(...) +#endif + +#ifndef NO_THREAD_SAFETY_ANALYSIS +#define NO_THREAD_SAFETY_ANALYSIS +#endif + +#endif // STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/win/stdint.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/win/stdint.h new file mode 100644 index 0000000..39edd0d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/win/stdint.h @@ -0,0 +1,24 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// MSVC didn't ship with this file until the 2010 version. + +#ifndef STORAGE_LEVELDB_PORT_WIN_STDINT_H_ +#define STORAGE_LEVELDB_PORT_WIN_STDINT_H_ + +#if !defined(_MSC_VER) +#error This file should only be included when compiling with MSVC. +#endif + +// Define C99 equivalent types. +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +#endif // STORAGE_LEVELDB_PORT_WIN_STDINT_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block.cc new file mode 100644 index 0000000..43e402c --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block.cc @@ -0,0 +1,268 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Decodes the blocks generated by block_builder.cc. + +#include "table/block.h" + +#include +#include +#include "leveldb/comparator.h" +#include "table/format.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +inline uint32_t Block::NumRestarts() const { + assert(size_ >= sizeof(uint32_t)); + return DecodeFixed32(data_ + size_ - sizeof(uint32_t)); +} + +Block::Block(const BlockContents& contents) + : data_(contents.data.data()), + size_(contents.data.size()), + owned_(contents.heap_allocated) { + if (size_ < sizeof(uint32_t)) { + size_ = 0; // Error marker + } else { + size_t max_restarts_allowed = (size_-sizeof(uint32_t)) / sizeof(uint32_t); + if (NumRestarts() > max_restarts_allowed) { + // The size is too small for NumRestarts() + size_ = 0; + } else { + restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t); + } + } +} + +Block::~Block() { + if (owned_) { + delete[] data_; + } +} + +// Helper routine: decode the next block entry starting at "p", +// storing the number of shared key bytes, non_shared key bytes, +// and the length of the value in "*shared", "*non_shared", and +// "*value_length", respectively. Will not dereference past "limit". +// +// If any errors are detected, returns NULL. Otherwise, returns a +// pointer to the key delta (just past the three decoded values). +static inline const char* DecodeEntry(const char* p, const char* limit, + uint32_t* shared, + uint32_t* non_shared, + uint32_t* value_length) { + if (limit - p < 3) return NULL; + *shared = reinterpret_cast(p)[0]; + *non_shared = reinterpret_cast(p)[1]; + *value_length = reinterpret_cast(p)[2]; + if ((*shared | *non_shared | *value_length) < 128) { + // Fast path: all three values are encoded in one byte each + p += 3; + } else { + if ((p = GetVarint32Ptr(p, limit, shared)) == NULL) return NULL; + if ((p = GetVarint32Ptr(p, limit, non_shared)) == NULL) return NULL; + if ((p = GetVarint32Ptr(p, limit, value_length)) == NULL) return NULL; + } + + if (static_cast(limit - p) < (*non_shared + *value_length)) { + return NULL; + } + return p; +} + +class Block::Iter : public Iterator { + private: + const Comparator* const comparator_; + const char* const data_; // underlying block contents + uint32_t const restarts_; // Offset of restart array (list of fixed32) + uint32_t const num_restarts_; // Number of uint32_t entries in restart array + + // current_ is offset in data_ of current entry. >= restarts_ if !Valid + uint32_t current_; + uint32_t restart_index_; // Index of restart block in which current_ falls + std::string key_; + Slice value_; + Status status_; + + inline int Compare(const Slice& a, const Slice& b) const { + return comparator_->Compare(a, b); + } + + // Return the offset in data_ just past the end of the current entry. + inline uint32_t NextEntryOffset() const { + return (value_.data() + value_.size()) - data_; + } + + uint32_t GetRestartPoint(uint32_t index) { + assert(index < num_restarts_); + return DecodeFixed32(data_ + restarts_ + index * sizeof(uint32_t)); + } + + void SeekToRestartPoint(uint32_t index) { + key_.clear(); + restart_index_ = index; + // current_ will be fixed by ParseNextKey(); + + // ParseNextKey() starts at the end of value_, so set value_ accordingly + uint32_t offset = GetRestartPoint(index); + value_ = Slice(data_ + offset, 0); + } + + public: + Iter(const Comparator* comparator, + const char* data, + uint32_t restarts, + uint32_t num_restarts) + : comparator_(comparator), + data_(data), + restarts_(restarts), + num_restarts_(num_restarts), + current_(restarts_), + restart_index_(num_restarts_) { + assert(num_restarts_ > 0); + } + + virtual bool Valid() const { return current_ < restarts_; } + virtual Status status() const { return status_; } + virtual Slice key() const { + assert(Valid()); + return key_; + } + virtual Slice value() const { + assert(Valid()); + return value_; + } + + virtual void Next() { + assert(Valid()); + ParseNextKey(); + } + + virtual void Prev() { + assert(Valid()); + + // Scan backwards to a restart point before current_ + const uint32_t original = current_; + while (GetRestartPoint(restart_index_) >= original) { + if (restart_index_ == 0) { + // No more entries + current_ = restarts_; + restart_index_ = num_restarts_; + return; + } + restart_index_--; + } + + SeekToRestartPoint(restart_index_); + do { + // Loop until end of current entry hits the start of original entry + } while (ParseNextKey() && NextEntryOffset() < original); + } + + virtual void Seek(const Slice& target) { + // Binary search in restart array to find the last restart point + // with a key < target + uint32_t left = 0; + uint32_t right = num_restarts_ - 1; + while (left < right) { + uint32_t mid = (left + right + 1) / 2; + uint32_t region_offset = GetRestartPoint(mid); + uint32_t shared, non_shared, value_length; + const char* key_ptr = DecodeEntry(data_ + region_offset, + data_ + restarts_, + &shared, &non_shared, &value_length); + if (key_ptr == NULL || (shared != 0)) { + CorruptionError(); + return; + } + Slice mid_key(key_ptr, non_shared); + if (Compare(mid_key, target) < 0) { + // Key at "mid" is smaller than "target". Therefore all + // blocks before "mid" are uninteresting. + left = mid; + } else { + // Key at "mid" is >= "target". Therefore all blocks at or + // after "mid" are uninteresting. + right = mid - 1; + } + } + + // Linear search (within restart block) for first key >= target + SeekToRestartPoint(left); + while (true) { + if (!ParseNextKey()) { + return; + } + if (Compare(key_, target) >= 0) { + return; + } + } + } + + virtual void SeekToFirst() { + SeekToRestartPoint(0); + ParseNextKey(); + } + + virtual void SeekToLast() { + SeekToRestartPoint(num_restarts_ - 1); + while (ParseNextKey() && NextEntryOffset() < restarts_) { + // Keep skipping + } + } + + private: + void CorruptionError() { + current_ = restarts_; + restart_index_ = num_restarts_; + status_ = Status::Corruption("bad entry in block"); + key_.clear(); + value_.clear(); + } + + bool ParseNextKey() { + current_ = NextEntryOffset(); + const char* p = data_ + current_; + const char* limit = data_ + restarts_; // Restarts come right after data + if (p >= limit) { + // No more entries to return. Mark as invalid. + current_ = restarts_; + restart_index_ = num_restarts_; + return false; + } + + // Decode next entry + uint32_t shared, non_shared, value_length; + p = DecodeEntry(p, limit, &shared, &non_shared, &value_length); + if (p == NULL || key_.size() < shared) { + CorruptionError(); + return false; + } else { + key_.resize(shared); + key_.append(p, non_shared); + value_ = Slice(p + non_shared, value_length); + while (restart_index_ + 1 < num_restarts_ && + GetRestartPoint(restart_index_ + 1) < current_) { + ++restart_index_; + } + return true; + } + } +}; + +Iterator* Block::NewIterator(const Comparator* cmp) { + if (size_ < sizeof(uint32_t)) { + return NewErrorIterator(Status::Corruption("bad block contents")); + } + const uint32_t num_restarts = NumRestarts(); + if (num_restarts == 0) { + return NewEmptyIterator(); + } else { + return new Iter(cmp, data_, restart_offset_, num_restarts); + } +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block.h new file mode 100644 index 0000000..2493eb9 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block.h @@ -0,0 +1,44 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_BLOCK_H_ +#define STORAGE_LEVELDB_TABLE_BLOCK_H_ + +#include +#include +#include "leveldb/iterator.h" + +namespace leveldb { + +struct BlockContents; +class Comparator; + +class Block { + public: + // Initialize the block with the specified contents. + explicit Block(const BlockContents& contents); + + ~Block(); + + size_t size() const { return size_; } + Iterator* NewIterator(const Comparator* comparator); + + private: + uint32_t NumRestarts() const; + + const char* data_; + size_t size_; + uint32_t restart_offset_; // Offset in data_ of restart array + bool owned_; // Block owns data_[] + + // No copying allowed + Block(const Block&); + void operator=(const Block&); + + class Iter; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_BLOCK_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block_builder.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block_builder.cc new file mode 100644 index 0000000..db660cd --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block_builder.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// BlockBuilder generates blocks where keys are prefix-compressed: +// +// When we store a key, we drop the prefix shared with the previous +// string. This helps reduce the space requirement significantly. +// Furthermore, once every K keys, we do not apply the prefix +// compression and store the entire key. We call this a "restart +// point". The tail end of the block stores the offsets of all of the +// restart points, and can be used to do a binary search when looking +// for a particular key. Values are stored as-is (without compression) +// immediately following the corresponding key. +// +// An entry for a particular key-value pair has the form: +// shared_bytes: varint32 +// unshared_bytes: varint32 +// value_length: varint32 +// key_delta: char[unshared_bytes] +// value: char[value_length] +// shared_bytes == 0 for restart points. +// +// The trailer of the block has the form: +// restarts: uint32[num_restarts] +// num_restarts: uint32 +// restarts[i] contains the offset within the block of the ith restart point. + +#include "table/block_builder.h" + +#include +#include +#include "leveldb/comparator.h" +#include "leveldb/table_builder.h" +#include "util/coding.h" + +namespace leveldb { + +BlockBuilder::BlockBuilder(const Options* options) + : options_(options), + restarts_(), + counter_(0), + finished_(false) { + assert(options->block_restart_interval >= 1); + restarts_.push_back(0); // First restart point is at offset 0 +} + +void BlockBuilder::Reset() { + buffer_.clear(); + restarts_.clear(); + restarts_.push_back(0); // First restart point is at offset 0 + counter_ = 0; + finished_ = false; + last_key_.clear(); +} + +size_t BlockBuilder::CurrentSizeEstimate() const { + return (buffer_.size() + // Raw data buffer + restarts_.size() * sizeof(uint32_t) + // Restart array + sizeof(uint32_t)); // Restart array length +} + +Slice BlockBuilder::Finish() { + // Append restart array + for (size_t i = 0; i < restarts_.size(); i++) { + PutFixed32(&buffer_, restarts_[i]); + } + PutFixed32(&buffer_, restarts_.size()); + finished_ = true; + return Slice(buffer_); +} + +void BlockBuilder::Add(const Slice& key, const Slice& value) { + Slice last_key_piece(last_key_); + assert(!finished_); + assert(counter_ <= options_->block_restart_interval); + assert(buffer_.empty() // No values yet? + || options_->comparator->Compare(key, last_key_piece) > 0); + size_t shared = 0; + if (counter_ < options_->block_restart_interval) { + // See how much sharing to do with previous string + const size_t min_length = std::min(last_key_piece.size(), key.size()); + while ((shared < min_length) && (last_key_piece[shared] == key[shared])) { + shared++; + } + } else { + // Restart compression + restarts_.push_back(buffer_.size()); + counter_ = 0; + } + const size_t non_shared = key.size() - shared; + + // Add "" to buffer_ + PutVarint32(&buffer_, shared); + PutVarint32(&buffer_, non_shared); + PutVarint32(&buffer_, value.size()); + + // Add string delta to buffer_ followed by value + buffer_.append(key.data() + shared, non_shared); + buffer_.append(value.data(), value.size()); + + // Update state + last_key_.resize(shared); + last_key_.append(key.data() + shared, non_shared); + assert(Slice(last_key_) == key); + counter_++; +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block_builder.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block_builder.h new file mode 100644 index 0000000..4fbcb33 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block_builder.h @@ -0,0 +1,57 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ +#define STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ + +#include + +#include +#include "leveldb/slice.h" + +namespace leveldb { + +struct Options; + +class BlockBuilder { + public: + explicit BlockBuilder(const Options* options); + + // Reset the contents as if the BlockBuilder was just constructed. + void Reset(); + + // REQUIRES: Finish() has not been called since the last call to Reset(). + // REQUIRES: key is larger than any previously added key + void Add(const Slice& key, const Slice& value); + + // Finish building the block and return a slice that refers to the + // block contents. The returned slice will remain valid for the + // lifetime of this builder or until Reset() is called. + Slice Finish(); + + // Returns an estimate of the current (uncompressed) size of the block + // we are building. + size_t CurrentSizeEstimate() const; + + // Return true iff no entries have been added since the last Reset() + bool empty() const { + return buffer_.empty(); + } + + private: + const Options* options_; + std::string buffer_; // Destination buffer + std::vector restarts_; // Restart points + int counter_; // Number of entries emitted since restart + bool finished_; // Has Finish() been called? + std::string last_key_; + + // No copying allowed + BlockBuilder(const BlockBuilder&); + void operator=(const BlockBuilder&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/filter_block.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/filter_block.cc new file mode 100644 index 0000000..1ed5134 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/filter_block.cc @@ -0,0 +1,111 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/filter_block.h" + +#include "leveldb/filter_policy.h" +#include "util/coding.h" + +namespace leveldb { + +// See doc/table_format.md for an explanation of the filter block format. + +// Generate new filter every 2KB of data +static const size_t kFilterBaseLg = 11; +static const size_t kFilterBase = 1 << kFilterBaseLg; + +FilterBlockBuilder::FilterBlockBuilder(const FilterPolicy* policy) + : policy_(policy) { +} + +void FilterBlockBuilder::StartBlock(uint64_t block_offset) { + uint64_t filter_index = (block_offset / kFilterBase); + assert(filter_index >= filter_offsets_.size()); + while (filter_index > filter_offsets_.size()) { + GenerateFilter(); + } +} + +void FilterBlockBuilder::AddKey(const Slice& key) { + Slice k = key; + start_.push_back(keys_.size()); + keys_.append(k.data(), k.size()); +} + +Slice FilterBlockBuilder::Finish() { + if (!start_.empty()) { + GenerateFilter(); + } + + // Append array of per-filter offsets + const uint32_t array_offset = result_.size(); + for (size_t i = 0; i < filter_offsets_.size(); i++) { + PutFixed32(&result_, filter_offsets_[i]); + } + + PutFixed32(&result_, array_offset); + result_.push_back(kFilterBaseLg); // Save encoding parameter in result + return Slice(result_); +} + +void FilterBlockBuilder::GenerateFilter() { + const size_t num_keys = start_.size(); + if (num_keys == 0) { + // Fast path if there are no keys for this filter + filter_offsets_.push_back(result_.size()); + return; + } + + // Make list of keys from flattened key structure + start_.push_back(keys_.size()); // Simplify length computation + tmp_keys_.resize(num_keys); + for (size_t i = 0; i < num_keys; i++) { + const char* base = keys_.data() + start_[i]; + size_t length = start_[i+1] - start_[i]; + tmp_keys_[i] = Slice(base, length); + } + + // Generate filter for current set of keys and append to result_. + filter_offsets_.push_back(result_.size()); + policy_->CreateFilter(&tmp_keys_[0], static_cast(num_keys), &result_); + + tmp_keys_.clear(); + keys_.clear(); + start_.clear(); +} + +FilterBlockReader::FilterBlockReader(const FilterPolicy* policy, + const Slice& contents) + : policy_(policy), + data_(NULL), + offset_(NULL), + num_(0), + base_lg_(0) { + size_t n = contents.size(); + if (n < 5) return; // 1 byte for base_lg_ and 4 for start of offset array + base_lg_ = contents[n-1]; + uint32_t last_word = DecodeFixed32(contents.data() + n - 5); + if (last_word > n - 5) return; + data_ = contents.data(); + offset_ = data_ + last_word; + num_ = (n - 5 - last_word) / 4; +} + +bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) { + uint64_t index = block_offset >> base_lg_; + if (index < num_) { + uint32_t start = DecodeFixed32(offset_ + index*4); + uint32_t limit = DecodeFixed32(offset_ + index*4 + 4); + if (start <= limit && limit <= static_cast(offset_ - data_)) { + Slice filter = Slice(data_ + start, limit - start); + return policy_->KeyMayMatch(key, filter); + } else if (start == limit) { + // Empty filters do not match any keys + return false; + } + } + return true; // Errors are treated as potential matches +} + +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/filter_block.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/filter_block.h new file mode 100644 index 0000000..c67d010 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/filter_block.h @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A filter block is stored near the end of a Table file. It contains +// filters (e.g., bloom filters) for all data blocks in the table combined +// into a single filter block. + +#ifndef STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ +#define STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ + +#include +#include +#include +#include +#include "leveldb/slice.h" +#include "util/hash.h" + +namespace leveldb { + +class FilterPolicy; + +// A FilterBlockBuilder is used to construct all of the filters for a +// particular Table. It generates a single string which is stored as +// a special block in the Table. +// +// The sequence of calls to FilterBlockBuilder must match the regexp: +// (StartBlock AddKey*)* Finish +class FilterBlockBuilder { + public: + explicit FilterBlockBuilder(const FilterPolicy*); + + void StartBlock(uint64_t block_offset); + void AddKey(const Slice& key); + Slice Finish(); + + private: + void GenerateFilter(); + + const FilterPolicy* policy_; + std::string keys_; // Flattened key contents + std::vector start_; // Starting index in keys_ of each key + std::string result_; // Filter data computed so far + std::vector tmp_keys_; // policy_->CreateFilter() argument + std::vector filter_offsets_; + + // No copying allowed + FilterBlockBuilder(const FilterBlockBuilder&); + void operator=(const FilterBlockBuilder&); +}; + +class FilterBlockReader { + public: + // REQUIRES: "contents" and *policy must stay live while *this is live. + FilterBlockReader(const FilterPolicy* policy, const Slice& contents); + bool KeyMayMatch(uint64_t block_offset, const Slice& key); + + private: + const FilterPolicy* policy_; + const char* data_; // Pointer to filter data (at block-start) + const char* offset_; // Pointer to beginning of offset array (at block-end) + size_t num_; // Number of entries in offset array + size_t base_lg_; // Encoding parameter (see kFilterBaseLg in .cc file) +}; + +} + +#endif // STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/filter_block_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/filter_block_test.cc new file mode 100644 index 0000000..8c4a474 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/filter_block_test.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/filter_block.h" + +#include "leveldb/filter_policy.h" +#include "util/coding.h" +#include "util/hash.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +// For testing: emit an array with one hash value per key +class TestHashFilter : public FilterPolicy { + public: + virtual const char* Name() const { + return "TestHashFilter"; + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + for (int i = 0; i < n; i++) { + uint32_t h = Hash(keys[i].data(), keys[i].size(), 1); + PutFixed32(dst, h); + } + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { + uint32_t h = Hash(key.data(), key.size(), 1); + for (size_t i = 0; i + 4 <= filter.size(); i += 4) { + if (h == DecodeFixed32(filter.data() + i)) { + return true; + } + } + return false; + } +}; + +class FilterBlockTest { + public: + TestHashFilter policy_; +}; + +TEST(FilterBlockTest, EmptyBuilder) { + FilterBlockBuilder builder(&policy_); + Slice block = builder.Finish(); + ASSERT_EQ("\\x00\\x00\\x00\\x00\\x0b", EscapeString(block)); + FilterBlockReader reader(&policy_, block); + ASSERT_TRUE(reader.KeyMayMatch(0, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(100000, "foo")); +} + +TEST(FilterBlockTest, SingleChunk) { + FilterBlockBuilder builder(&policy_); + builder.StartBlock(100); + builder.AddKey("foo"); + builder.AddKey("bar"); + builder.AddKey("box"); + builder.StartBlock(200); + builder.AddKey("box"); + builder.StartBlock(300); + builder.AddKey("hello"); + Slice block = builder.Finish(); + FilterBlockReader reader(&policy_, block); + ASSERT_TRUE(reader.KeyMayMatch(100, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(100, "bar")); + ASSERT_TRUE(reader.KeyMayMatch(100, "box")); + ASSERT_TRUE(reader.KeyMayMatch(100, "hello")); + ASSERT_TRUE(reader.KeyMayMatch(100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(100, "missing")); + ASSERT_TRUE(! reader.KeyMayMatch(100, "other")); +} + +TEST(FilterBlockTest, MultiChunk) { + FilterBlockBuilder builder(&policy_); + + // First filter + builder.StartBlock(0); + builder.AddKey("foo"); + builder.StartBlock(2000); + builder.AddKey("bar"); + + // Second filter + builder.StartBlock(3100); + builder.AddKey("box"); + + // Third filter is empty + + // Last filter + builder.StartBlock(9000); + builder.AddKey("box"); + builder.AddKey("hello"); + + Slice block = builder.Finish(); + FilterBlockReader reader(&policy_, block); + + // Check first filter + ASSERT_TRUE(reader.KeyMayMatch(0, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(2000, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(0, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(0, "hello")); + + // Check second filter + ASSERT_TRUE(reader.KeyMayMatch(3100, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "hello")); + + // Check third filter (empty) + ASSERT_TRUE(! reader.KeyMayMatch(4100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "hello")); + + // Check last filter + ASSERT_TRUE(reader.KeyMayMatch(9000, "box")); + ASSERT_TRUE(reader.KeyMayMatch(9000, "hello")); + ASSERT_TRUE(! reader.KeyMayMatch(9000, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(9000, "bar")); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/format.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/format.cc new file mode 100644 index 0000000..24e4e02 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/format.cc @@ -0,0 +1,144 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/format.h" + +#include "leveldb/env.h" +#include "port/port.h" +#include "table/block.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { + +void BlockHandle::EncodeTo(std::string* dst) const { + // Sanity check that all fields have been set + assert(offset_ != ~static_cast(0)); + assert(size_ != ~static_cast(0)); + PutVarint64(dst, offset_); + PutVarint64(dst, size_); +} + +Status BlockHandle::DecodeFrom(Slice* input) { + if (GetVarint64(input, &offset_) && + GetVarint64(input, &size_)) { + return Status::OK(); + } else { + return Status::Corruption("bad block handle"); + } +} + +void Footer::EncodeTo(std::string* dst) const { + const size_t original_size = dst->size(); + metaindex_handle_.EncodeTo(dst); + index_handle_.EncodeTo(dst); + dst->resize(2 * BlockHandle::kMaxEncodedLength); // Padding + PutFixed32(dst, static_cast(kTableMagicNumber & 0xffffffffu)); + PutFixed32(dst, static_cast(kTableMagicNumber >> 32)); + assert(dst->size() == original_size + kEncodedLength); + (void)original_size; // Disable unused variable warning. +} + +Status Footer::DecodeFrom(Slice* input) { + const char* magic_ptr = input->data() + kEncodedLength - 8; + const uint32_t magic_lo = DecodeFixed32(magic_ptr); + const uint32_t magic_hi = DecodeFixed32(magic_ptr + 4); + const uint64_t magic = ((static_cast(magic_hi) << 32) | + (static_cast(magic_lo))); + if (magic != kTableMagicNumber) { + return Status::Corruption("not an sstable (bad magic number)"); + } + + Status result = metaindex_handle_.DecodeFrom(input); + if (result.ok()) { + result = index_handle_.DecodeFrom(input); + } + if (result.ok()) { + // We skip over any leftover data (just padding for now) in "input" + const char* end = magic_ptr + 8; + *input = Slice(end, input->data() + input->size() - end); + } + return result; +} + +Status ReadBlock(RandomAccessFile* file, + const ReadOptions& options, + const BlockHandle& handle, + BlockContents* result) { + result->data = Slice(); + result->cachable = false; + result->heap_allocated = false; + + // Read the block contents as well as the type/crc footer. + // See table_builder.cc for the code that built this structure. + size_t n = static_cast(handle.size()); + char* buf = new char[n + kBlockTrailerSize]; + Slice contents; + Status s = file->Read(handle.offset(), n + kBlockTrailerSize, &contents, buf); + if (!s.ok()) { + delete[] buf; + return s; + } + if (contents.size() != n + kBlockTrailerSize) { + delete[] buf; + return Status::Corruption("truncated block read"); + } + + // Check the crc of the type and the block contents + const char* data = contents.data(); // Pointer to where Read put the data + if (options.verify_checksums) { + const uint32_t crc = crc32c::Unmask(DecodeFixed32(data + n + 1)); + const uint32_t actual = crc32c::Value(data, n + 1); + if (actual != crc) { + delete[] buf; + s = Status::Corruption("block checksum mismatch"); + return s; + } + } + + switch (data[n]) { + case kNoCompression: + if (data != buf) { + // File implementation gave us pointer to some other data. + // Use it directly under the assumption that it will be live + // while the file is open. + delete[] buf; + result->data = Slice(data, n); + result->heap_allocated = false; + result->cachable = false; // Do not double-cache + } else { + result->data = Slice(buf, n); + result->heap_allocated = true; + result->cachable = true; + } + + // Ok + break; + case kSnappyCompression: { + size_t ulength = 0; + if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) { + delete[] buf; + return Status::Corruption("corrupted compressed block contents"); + } + char* ubuf = new char[ulength]; + if (!port::Snappy_Uncompress(data, n, ubuf)) { + delete[] buf; + delete[] ubuf; + return Status::Corruption("corrupted compressed block contents"); + } + delete[] buf; + result->data = Slice(ubuf, ulength); + result->heap_allocated = true; + result->cachable = true; + break; + } + default: + delete[] buf; + return Status::Corruption("bad block type"); + } + + return Status::OK(); +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/format.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/format.h new file mode 100644 index 0000000..6c0b80c --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/format.h @@ -0,0 +1,108 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_FORMAT_H_ +#define STORAGE_LEVELDB_TABLE_FORMAT_H_ + +#include +#include +#include "leveldb/slice.h" +#include "leveldb/status.h" +#include "leveldb/table_builder.h" + +namespace leveldb { + +class Block; +class RandomAccessFile; +struct ReadOptions; + +// BlockHandle is a pointer to the extent of a file that stores a data +// block or a meta block. +class BlockHandle { + public: + BlockHandle(); + + // The offset of the block in the file. + uint64_t offset() const { return offset_; } + void set_offset(uint64_t offset) { offset_ = offset; } + + // The size of the stored block + uint64_t size() const { return size_; } + void set_size(uint64_t size) { size_ = size; } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(Slice* input); + + // Maximum encoding length of a BlockHandle + enum { kMaxEncodedLength = 10 + 10 }; + + private: + uint64_t offset_; + uint64_t size_; +}; + +// Footer encapsulates the fixed information stored at the tail +// end of every table file. +class Footer { + public: + Footer() { } + + // The block handle for the metaindex block of the table + const BlockHandle& metaindex_handle() const { return metaindex_handle_; } + void set_metaindex_handle(const BlockHandle& h) { metaindex_handle_ = h; } + + // The block handle for the index block of the table + const BlockHandle& index_handle() const { + return index_handle_; + } + void set_index_handle(const BlockHandle& h) { + index_handle_ = h; + } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(Slice* input); + + // Encoded length of a Footer. Note that the serialization of a + // Footer will always occupy exactly this many bytes. It consists + // of two block handles and a magic number. + enum { + kEncodedLength = 2*BlockHandle::kMaxEncodedLength + 8 + }; + + private: + BlockHandle metaindex_handle_; + BlockHandle index_handle_; +}; + +// kTableMagicNumber was picked by running +// echo http://code.google.com/p/leveldb/ | sha1sum +// and taking the leading 64 bits. +static const uint64_t kTableMagicNumber = 0xdb4775248b80fb57ull; + +// 1-byte type + 32-bit crc +static const size_t kBlockTrailerSize = 5; + +struct BlockContents { + Slice data; // Actual contents of data + bool cachable; // True iff data can be cached + bool heap_allocated; // True iff caller should delete[] data.data() +}; + +// Read the block identified by "handle" from "file". On failure +// return non-OK. On success fill *result and return OK. +extern Status ReadBlock(RandomAccessFile* file, + const ReadOptions& options, + const BlockHandle& handle, + BlockContents* result); + +// Implementation details follow. Clients should ignore, + +inline BlockHandle::BlockHandle() + : offset_(~static_cast(0)), + size_(~static_cast(0)) { +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_FORMAT_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/iterator.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/iterator.cc new file mode 100644 index 0000000..3d1c87f --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/iterator.cc @@ -0,0 +1,67 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/iterator.h" + +namespace leveldb { + +Iterator::Iterator() { + cleanup_.function = NULL; + cleanup_.next = NULL; +} + +Iterator::~Iterator() { + if (cleanup_.function != NULL) { + (*cleanup_.function)(cleanup_.arg1, cleanup_.arg2); + for (Cleanup* c = cleanup_.next; c != NULL; ) { + (*c->function)(c->arg1, c->arg2); + Cleanup* next = c->next; + delete c; + c = next; + } + } +} + +void Iterator::RegisterCleanup(CleanupFunction func, void* arg1, void* arg2) { + assert(func != NULL); + Cleanup* c; + if (cleanup_.function == NULL) { + c = &cleanup_; + } else { + c = new Cleanup; + c->next = cleanup_.next; + cleanup_.next = c; + } + c->function = func; + c->arg1 = arg1; + c->arg2 = arg2; +} + +namespace { +class EmptyIterator : public Iterator { + public: + EmptyIterator(const Status& s) : status_(s) { } + virtual bool Valid() const { return false; } + virtual void Seek(const Slice& target) { } + virtual void SeekToFirst() { } + virtual void SeekToLast() { } + virtual void Next() { assert(false); } + virtual void Prev() { assert(false); } + Slice key() const { assert(false); return Slice(); } + Slice value() const { assert(false); return Slice(); } + virtual Status status() const { return status_; } + private: + Status status_; +}; +} // namespace + +Iterator* NewEmptyIterator() { + return new EmptyIterator(Status::OK()); +} + +Iterator* NewErrorIterator(const Status& status) { + return new EmptyIterator(status); +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/iterator_wrapper.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/iterator_wrapper.h new file mode 100644 index 0000000..f410c3f --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/iterator_wrapper.h @@ -0,0 +1,66 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ +#define STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ + +#include "leveldb/iterator.h" +#include "leveldb/slice.h" + +namespace leveldb { + +// A internal wrapper class with an interface similar to Iterator that +// caches the valid() and key() results for an underlying iterator. +// This can help avoid virtual function calls and also gives better +// cache locality. +class IteratorWrapper { + public: + IteratorWrapper(): iter_(NULL), valid_(false) { } + explicit IteratorWrapper(Iterator* iter): iter_(NULL) { + Set(iter); + } + ~IteratorWrapper() { delete iter_; } + Iterator* iter() const { return iter_; } + + // Takes ownership of "iter" and will delete it when destroyed, or + // when Set() is invoked again. + void Set(Iterator* iter) { + delete iter_; + iter_ = iter; + if (iter_ == NULL) { + valid_ = false; + } else { + Update(); + } + } + + + // Iterator interface methods + bool Valid() const { return valid_; } + Slice key() const { assert(Valid()); return key_; } + Slice value() const { assert(Valid()); return iter_->value(); } + // Methods below require iter() != NULL + Status status() const { assert(iter_); return iter_->status(); } + void Next() { assert(iter_); iter_->Next(); Update(); } + void Prev() { assert(iter_); iter_->Prev(); Update(); } + void Seek(const Slice& k) { assert(iter_); iter_->Seek(k); Update(); } + void SeekToFirst() { assert(iter_); iter_->SeekToFirst(); Update(); } + void SeekToLast() { assert(iter_); iter_->SeekToLast(); Update(); } + + private: + void Update() { + valid_ = iter_->Valid(); + if (valid_) { + key_ = iter_->key(); + } + } + + Iterator* iter_; + bool valid_; + Slice key_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/merger.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/merger.cc new file mode 100644 index 0000000..2dde4dc --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/merger.cc @@ -0,0 +1,197 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/merger.h" + +#include "leveldb/comparator.h" +#include "leveldb/iterator.h" +#include "table/iterator_wrapper.h" + +namespace leveldb { + +namespace { +class MergingIterator : public Iterator { + public: + MergingIterator(const Comparator* comparator, Iterator** children, int n) + : comparator_(comparator), + children_(new IteratorWrapper[n]), + n_(n), + current_(NULL), + direction_(kForward) { + for (int i = 0; i < n; i++) { + children_[i].Set(children[i]); + } + } + + virtual ~MergingIterator() { + delete[] children_; + } + + virtual bool Valid() const { + return (current_ != NULL); + } + + virtual void SeekToFirst() { + for (int i = 0; i < n_; i++) { + children_[i].SeekToFirst(); + } + FindSmallest(); + direction_ = kForward; + } + + virtual void SeekToLast() { + for (int i = 0; i < n_; i++) { + children_[i].SeekToLast(); + } + FindLargest(); + direction_ = kReverse; + } + + virtual void Seek(const Slice& target) { + for (int i = 0; i < n_; i++) { + children_[i].Seek(target); + } + FindSmallest(); + direction_ = kForward; + } + + virtual void Next() { + assert(Valid()); + + // Ensure that all children are positioned after key(). + // If we are moving in the forward direction, it is already + // true for all of the non-current_ children since current_ is + // the smallest child and key() == current_->key(). Otherwise, + // we explicitly position the non-current_ children. + if (direction_ != kForward) { + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child != current_) { + child->Seek(key()); + if (child->Valid() && + comparator_->Compare(key(), child->key()) == 0) { + child->Next(); + } + } + } + direction_ = kForward; + } + + current_->Next(); + FindSmallest(); + } + + virtual void Prev() { + assert(Valid()); + + // Ensure that all children are positioned before key(). + // If we are moving in the reverse direction, it is already + // true for all of the non-current_ children since current_ is + // the largest child and key() == current_->key(). Otherwise, + // we explicitly position the non-current_ children. + if (direction_ != kReverse) { + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child != current_) { + child->Seek(key()); + if (child->Valid()) { + // Child is at first entry >= key(). Step back one to be < key() + child->Prev(); + } else { + // Child has no entries >= key(). Position at last entry. + child->SeekToLast(); + } + } + } + direction_ = kReverse; + } + + current_->Prev(); + FindLargest(); + } + + virtual Slice key() const { + assert(Valid()); + return current_->key(); + } + + virtual Slice value() const { + assert(Valid()); + return current_->value(); + } + + virtual Status status() const { + Status status; + for (int i = 0; i < n_; i++) { + status = children_[i].status(); + if (!status.ok()) { + break; + } + } + return status; + } + + private: + void FindSmallest(); + void FindLargest(); + + // We might want to use a heap in case there are lots of children. + // For now we use a simple array since we expect a very small number + // of children in leveldb. + const Comparator* comparator_; + IteratorWrapper* children_; + int n_; + IteratorWrapper* current_; + + // Which direction is the iterator moving? + enum Direction { + kForward, + kReverse + }; + Direction direction_; +}; + +void MergingIterator::FindSmallest() { + IteratorWrapper* smallest = NULL; + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child->Valid()) { + if (smallest == NULL) { + smallest = child; + } else if (comparator_->Compare(child->key(), smallest->key()) < 0) { + smallest = child; + } + } + } + current_ = smallest; +} + +void MergingIterator::FindLargest() { + IteratorWrapper* largest = NULL; + for (int i = n_-1; i >= 0; i--) { + IteratorWrapper* child = &children_[i]; + if (child->Valid()) { + if (largest == NULL) { + largest = child; + } else if (comparator_->Compare(child->key(), largest->key()) > 0) { + largest = child; + } + } + } + current_ = largest; +} +} // namespace + +Iterator* NewMergingIterator(const Comparator* cmp, Iterator** list, int n) { + assert(n >= 0); + if (n == 0) { + return NewEmptyIterator(); + } else if (n == 1) { + return list[0]; + } else { + return new MergingIterator(cmp, list, n); + } +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/merger.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/merger.h new file mode 100644 index 0000000..91ddd80 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/merger.h @@ -0,0 +1,26 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_MERGER_H_ +#define STORAGE_LEVELDB_TABLE_MERGER_H_ + +namespace leveldb { + +class Comparator; +class Iterator; + +// Return an iterator that provided the union of the data in +// children[0,n-1]. Takes ownership of the child iterators and +// will delete them when the result iterator is deleted. +// +// The result does no duplicate suppression. I.e., if a particular +// key is present in K child iterators, it will be yielded K times. +// +// REQUIRES: n >= 0 +extern Iterator* NewMergingIterator( + const Comparator* comparator, Iterator** children, int n); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_MERGER_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/table.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/table.cc new file mode 100644 index 0000000..decf808 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/table.cc @@ -0,0 +1,285 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table.h" + +#include "leveldb/cache.h" +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/options.h" +#include "table/block.h" +#include "table/filter_block.h" +#include "table/format.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" + +namespace leveldb { + +struct Table::Rep { + ~Rep() { + delete filter; + delete [] filter_data; + delete index_block; + } + + Options options; + Status status; + RandomAccessFile* file; + uint64_t cache_id; + FilterBlockReader* filter; + const char* filter_data; + + BlockHandle metaindex_handle; // Handle to metaindex_block: saved from footer + Block* index_block; +}; + +Status Table::Open(const Options& options, + RandomAccessFile* file, + uint64_t size, + Table** table) { + *table = NULL; + if (size < Footer::kEncodedLength) { + return Status::Corruption("file is too short to be an sstable"); + } + + char footer_space[Footer::kEncodedLength]; + Slice footer_input; + Status s = file->Read(size - Footer::kEncodedLength, Footer::kEncodedLength, + &footer_input, footer_space); + if (!s.ok()) return s; + + Footer footer; + s = footer.DecodeFrom(&footer_input); + if (!s.ok()) return s; + + // Read the index block + BlockContents contents; + Block* index_block = NULL; + if (s.ok()) { + ReadOptions opt; + if (options.paranoid_checks) { + opt.verify_checksums = true; + } + s = ReadBlock(file, opt, footer.index_handle(), &contents); + if (s.ok()) { + index_block = new Block(contents); + } + } + + if (s.ok()) { + // We've successfully read the footer and the index block: we're + // ready to serve requests. + Rep* rep = new Table::Rep; + rep->options = options; + rep->file = file; + rep->metaindex_handle = footer.metaindex_handle(); + rep->index_block = index_block; + rep->cache_id = (options.block_cache ? options.block_cache->NewId() : 0); + rep->filter_data = NULL; + rep->filter = NULL; + *table = new Table(rep); + (*table)->ReadMeta(footer); + } else { + delete index_block; + } + + return s; +} + +void Table::ReadMeta(const Footer& footer) { + if (rep_->options.filter_policy == NULL) { + return; // Do not need any metadata + } + + // TODO(sanjay): Skip this if footer.metaindex_handle() size indicates + // it is an empty block. + ReadOptions opt; + if (rep_->options.paranoid_checks) { + opt.verify_checksums = true; + } + BlockContents contents; + if (!ReadBlock(rep_->file, opt, footer.metaindex_handle(), &contents).ok()) { + // Do not propagate errors since meta info is not needed for operation + return; + } + Block* meta = new Block(contents); + + Iterator* iter = meta->NewIterator(BytewiseComparator()); + std::string key = "filter."; + key.append(rep_->options.filter_policy->Name()); + iter->Seek(key); + if (iter->Valid() && iter->key() == Slice(key)) { + ReadFilter(iter->value()); + } + delete iter; + delete meta; +} + +void Table::ReadFilter(const Slice& filter_handle_value) { + Slice v = filter_handle_value; + BlockHandle filter_handle; + if (!filter_handle.DecodeFrom(&v).ok()) { + return; + } + + // We might want to unify with ReadBlock() if we start + // requiring checksum verification in Table::Open. + ReadOptions opt; + if (rep_->options.paranoid_checks) { + opt.verify_checksums = true; + } + BlockContents block; + if (!ReadBlock(rep_->file, opt, filter_handle, &block).ok()) { + return; + } + if (block.heap_allocated) { + rep_->filter_data = block.data.data(); // Will need to delete later + } + rep_->filter = new FilterBlockReader(rep_->options.filter_policy, block.data); +} + +Table::~Table() { + delete rep_; +} + +static void DeleteBlock(void* arg, void* ignored) { + delete reinterpret_cast(arg); +} + +static void DeleteCachedBlock(const Slice& key, void* value) { + Block* block = reinterpret_cast(value); + delete block; +} + +static void ReleaseBlock(void* arg, void* h) { + Cache* cache = reinterpret_cast(arg); + Cache::Handle* handle = reinterpret_cast(h); + cache->Release(handle); +} + +// Convert an index iterator value (i.e., an encoded BlockHandle) +// into an iterator over the contents of the corresponding block. +Iterator* Table::BlockReader(void* arg, + const ReadOptions& options, + const Slice& index_value) { + Table* table = reinterpret_cast(arg); + Cache* block_cache = table->rep_->options.block_cache; + Block* block = NULL; + Cache::Handle* cache_handle = NULL; + + BlockHandle handle; + Slice input = index_value; + Status s = handle.DecodeFrom(&input); + // We intentionally allow extra stuff in index_value so that we + // can add more features in the future. + + if (s.ok()) { + BlockContents contents; + if (block_cache != NULL) { + char cache_key_buffer[16]; + EncodeFixed64(cache_key_buffer, table->rep_->cache_id); + EncodeFixed64(cache_key_buffer+8, handle.offset()); + Slice key(cache_key_buffer, sizeof(cache_key_buffer)); + cache_handle = block_cache->Lookup(key); + if (cache_handle != NULL) { + block = reinterpret_cast(block_cache->Value(cache_handle)); + } else { + s = ReadBlock(table->rep_->file, options, handle, &contents); + if (s.ok()) { + block = new Block(contents); + if (contents.cachable && options.fill_cache) { + cache_handle = block_cache->Insert( + key, block, block->size(), &DeleteCachedBlock); + } + } + } + } else { + s = ReadBlock(table->rep_->file, options, handle, &contents); + if (s.ok()) { + block = new Block(contents); + } + } + } + + Iterator* iter; + if (block != NULL) { + iter = block->NewIterator(table->rep_->options.comparator); + if (cache_handle == NULL) { + iter->RegisterCleanup(&DeleteBlock, block, NULL); + } else { + iter->RegisterCleanup(&ReleaseBlock, block_cache, cache_handle); + } + } else { + iter = NewErrorIterator(s); + } + return iter; +} + +Iterator* Table::NewIterator(const ReadOptions& options) const { + return NewTwoLevelIterator( + rep_->index_block->NewIterator(rep_->options.comparator), + &Table::BlockReader, const_cast(this), options); +} + +Status Table::InternalGet(const ReadOptions& options, const Slice& k, + void* arg, + void (*saver)(void*, const Slice&, const Slice&)) { + Status s; + Iterator* iiter = rep_->index_block->NewIterator(rep_->options.comparator); + iiter->Seek(k); + if (iiter->Valid()) { + Slice handle_value = iiter->value(); + FilterBlockReader* filter = rep_->filter; + BlockHandle handle; + if (filter != NULL && + handle.DecodeFrom(&handle_value).ok() && + !filter->KeyMayMatch(handle.offset(), k)) { + // Not found + } else { + Iterator* block_iter = BlockReader(this, options, iiter->value()); + block_iter->Seek(k); + if (block_iter->Valid()) { + (*saver)(arg, block_iter->key(), block_iter->value()); + } + s = block_iter->status(); + delete block_iter; + } + } + if (s.ok()) { + s = iiter->status(); + } + delete iiter; + return s; +} + + +uint64_t Table::ApproximateOffsetOf(const Slice& key) const { + Iterator* index_iter = + rep_->index_block->NewIterator(rep_->options.comparator); + index_iter->Seek(key); + uint64_t result; + if (index_iter->Valid()) { + BlockHandle handle; + Slice input = index_iter->value(); + Status s = handle.DecodeFrom(&input); + if (s.ok()) { + result = handle.offset(); + } else { + // Strange: we can't decode the block handle in the index block. + // We'll just return the offset of the metaindex block, which is + // close to the whole file size for this case. + result = rep_->metaindex_handle.offset(); + } + } else { + // key is past the last key in the file. Approximate the offset + // by returning the offset of the metaindex block (which is + // right near the end of the file). + result = rep_->metaindex_handle.offset(); + } + delete index_iter; + return result; +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/table_builder.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/table_builder.cc new file mode 100644 index 0000000..62002c8 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/table_builder.cc @@ -0,0 +1,270 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table_builder.h" + +#include +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/options.h" +#include "table/block_builder.h" +#include "table/filter_block.h" +#include "table/format.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { + +struct TableBuilder::Rep { + Options options; + Options index_block_options; + WritableFile* file; + uint64_t offset; + Status status; + BlockBuilder data_block; + BlockBuilder index_block; + std::string last_key; + int64_t num_entries; + bool closed; // Either Finish() or Abandon() has been called. + FilterBlockBuilder* filter_block; + + // We do not emit the index entry for a block until we have seen the + // first key for the next data block. This allows us to use shorter + // keys in the index block. For example, consider a block boundary + // between the keys "the quick brown fox" and "the who". We can use + // "the r" as the key for the index block entry since it is >= all + // entries in the first block and < all entries in subsequent + // blocks. + // + // Invariant: r->pending_index_entry is true only if data_block is empty. + bool pending_index_entry; + BlockHandle pending_handle; // Handle to add to index block + + std::string compressed_output; + + Rep(const Options& opt, WritableFile* f) + : options(opt), + index_block_options(opt), + file(f), + offset(0), + data_block(&options), + index_block(&index_block_options), + num_entries(0), + closed(false), + filter_block(opt.filter_policy == NULL ? NULL + : new FilterBlockBuilder(opt.filter_policy)), + pending_index_entry(false) { + index_block_options.block_restart_interval = 1; + } +}; + +TableBuilder::TableBuilder(const Options& options, WritableFile* file) + : rep_(new Rep(options, file)) { + if (rep_->filter_block != NULL) { + rep_->filter_block->StartBlock(0); + } +} + +TableBuilder::~TableBuilder() { + assert(rep_->closed); // Catch errors where caller forgot to call Finish() + delete rep_->filter_block; + delete rep_; +} + +Status TableBuilder::ChangeOptions(const Options& options) { + // Note: if more fields are added to Options, update + // this function to catch changes that should not be allowed to + // change in the middle of building a Table. + if (options.comparator != rep_->options.comparator) { + return Status::InvalidArgument("changing comparator while building table"); + } + + // Note that any live BlockBuilders point to rep_->options and therefore + // will automatically pick up the updated options. + rep_->options = options; + rep_->index_block_options = options; + rep_->index_block_options.block_restart_interval = 1; + return Status::OK(); +} + +void TableBuilder::Add(const Slice& key, const Slice& value) { + Rep* r = rep_; + assert(!r->closed); + if (!ok()) return; + if (r->num_entries > 0) { + assert(r->options.comparator->Compare(key, Slice(r->last_key)) > 0); + } + + if (r->pending_index_entry) { + assert(r->data_block.empty()); + r->options.comparator->FindShortestSeparator(&r->last_key, key); + std::string handle_encoding; + r->pending_handle.EncodeTo(&handle_encoding); + r->index_block.Add(r->last_key, Slice(handle_encoding)); + r->pending_index_entry = false; + } + + if (r->filter_block != NULL) { + r->filter_block->AddKey(key); + } + + r->last_key.assign(key.data(), key.size()); + r->num_entries++; + r->data_block.Add(key, value); + + const size_t estimated_block_size = r->data_block.CurrentSizeEstimate(); + if (estimated_block_size >= r->options.block_size) { + Flush(); + } +} + +void TableBuilder::Flush() { + Rep* r = rep_; + assert(!r->closed); + if (!ok()) return; + if (r->data_block.empty()) return; + assert(!r->pending_index_entry); + WriteBlock(&r->data_block, &r->pending_handle); + if (ok()) { + r->pending_index_entry = true; + r->status = r->file->Flush(); + } + if (r->filter_block != NULL) { + r->filter_block->StartBlock(r->offset); + } +} + +void TableBuilder::WriteBlock(BlockBuilder* block, BlockHandle* handle) { + // File format contains a sequence of blocks where each block has: + // block_data: uint8[n] + // type: uint8 + // crc: uint32 + assert(ok()); + Rep* r = rep_; + Slice raw = block->Finish(); + + Slice block_contents; + CompressionType type = r->options.compression; + // TODO(postrelease): Support more compression options: zlib? + switch (type) { + case kNoCompression: + block_contents = raw; + break; + + case kSnappyCompression: { + std::string* compressed = &r->compressed_output; + if (port::Snappy_Compress(raw.data(), raw.size(), compressed) && + compressed->size() < raw.size() - (raw.size() / 8u)) { + block_contents = *compressed; + } else { + // Snappy not supported, or compressed less than 12.5%, so just + // store uncompressed form + block_contents = raw; + type = kNoCompression; + } + break; + } + } + WriteRawBlock(block_contents, type, handle); + r->compressed_output.clear(); + block->Reset(); +} + +void TableBuilder::WriteRawBlock(const Slice& block_contents, + CompressionType type, + BlockHandle* handle) { + Rep* r = rep_; + handle->set_offset(r->offset); + handle->set_size(block_contents.size()); + r->status = r->file->Append(block_contents); + if (r->status.ok()) { + char trailer[kBlockTrailerSize]; + trailer[0] = type; + uint32_t crc = crc32c::Value(block_contents.data(), block_contents.size()); + crc = crc32c::Extend(crc, trailer, 1); // Extend crc to cover block type + EncodeFixed32(trailer+1, crc32c::Mask(crc)); + r->status = r->file->Append(Slice(trailer, kBlockTrailerSize)); + if (r->status.ok()) { + r->offset += block_contents.size() + kBlockTrailerSize; + } + } +} + +Status TableBuilder::status() const { + return rep_->status; +} + +Status TableBuilder::Finish() { + Rep* r = rep_; + Flush(); + assert(!r->closed); + r->closed = true; + + BlockHandle filter_block_handle, metaindex_block_handle, index_block_handle; + + // Write filter block + if (ok() && r->filter_block != NULL) { + WriteRawBlock(r->filter_block->Finish(), kNoCompression, + &filter_block_handle); + } + + // Write metaindex block + if (ok()) { + BlockBuilder meta_index_block(&r->options); + if (r->filter_block != NULL) { + // Add mapping from "filter.Name" to location of filter data + std::string key = "filter."; + key.append(r->options.filter_policy->Name()); + std::string handle_encoding; + filter_block_handle.EncodeTo(&handle_encoding); + meta_index_block.Add(key, handle_encoding); + } + + // TODO(postrelease): Add stats and other meta blocks + WriteBlock(&meta_index_block, &metaindex_block_handle); + } + + // Write index block + if (ok()) { + if (r->pending_index_entry) { + r->options.comparator->FindShortSuccessor(&r->last_key); + std::string handle_encoding; + r->pending_handle.EncodeTo(&handle_encoding); + r->index_block.Add(r->last_key, Slice(handle_encoding)); + r->pending_index_entry = false; + } + WriteBlock(&r->index_block, &index_block_handle); + } + + // Write footer + if (ok()) { + Footer footer; + footer.set_metaindex_handle(metaindex_block_handle); + footer.set_index_handle(index_block_handle); + std::string footer_encoding; + footer.EncodeTo(&footer_encoding); + r->status = r->file->Append(footer_encoding); + if (r->status.ok()) { + r->offset += footer_encoding.size(); + } + } + return r->status; +} + +void TableBuilder::Abandon() { + Rep* r = rep_; + assert(!r->closed); + r->closed = true; +} + +uint64_t TableBuilder::NumEntries() const { + return rep_->num_entries; +} + +uint64_t TableBuilder::FileSize() const { + return rep_->offset; +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/table_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/table_test.cc new file mode 100644 index 0000000..abf6e24 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/table_test.cc @@ -0,0 +1,876 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table.h" + +#include +#include +#include "db/dbformat.h" +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "leveldb/table_builder.h" +#include "table/block.h" +#include "table/block_builder.h" +#include "table/format.h" +#include "util/random.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +// Return reverse of "key". +// Used to test non-lexicographic comparators. +static std::string Reverse(const Slice& key) { + std::string str(key.ToString()); + std::string rev(""); + for (std::string::reverse_iterator rit = str.rbegin(); + rit != str.rend(); ++rit) { + rev.push_back(*rit); + } + return rev; +} + +namespace { +class ReverseKeyComparator : public Comparator { + public: + virtual const char* Name() const { + return "leveldb.ReverseBytewiseComparator"; + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return BytewiseComparator()->Compare(Reverse(a), Reverse(b)); + } + + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const { + std::string s = Reverse(*start); + std::string l = Reverse(limit); + BytewiseComparator()->FindShortestSeparator(&s, l); + *start = Reverse(s); + } + + virtual void FindShortSuccessor(std::string* key) const { + std::string s = Reverse(*key); + BytewiseComparator()->FindShortSuccessor(&s); + *key = Reverse(s); + } +}; +} // namespace +static ReverseKeyComparator reverse_key_comparator; + +static void Increment(const Comparator* cmp, std::string* key) { + if (cmp == BytewiseComparator()) { + key->push_back('\0'); + } else { + assert(cmp == &reverse_key_comparator); + std::string rev = Reverse(*key); + rev.push_back('\0'); + *key = Reverse(rev); + } +} + +// An STL comparator that uses a Comparator +namespace { +struct STLLessThan { + const Comparator* cmp; + + STLLessThan() : cmp(BytewiseComparator()) { } + STLLessThan(const Comparator* c) : cmp(c) { } + bool operator()(const std::string& a, const std::string& b) const { + return cmp->Compare(Slice(a), Slice(b)) < 0; + } +}; +} // namespace + +class StringSink: public WritableFile { + public: + ~StringSink() { } + + const std::string& contents() const { return contents_; } + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + + virtual Status Append(const Slice& data) { + contents_.append(data.data(), data.size()); + return Status::OK(); + } + + private: + std::string contents_; +}; + + +class StringSource: public RandomAccessFile { + public: + StringSource(const Slice& contents) + : contents_(contents.data(), contents.size()) { + } + + virtual ~StringSource() { } + + uint64_t Size() const { return contents_.size(); } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + if (offset > contents_.size()) { + return Status::InvalidArgument("invalid Read offset"); + } + if (offset + n > contents_.size()) { + n = contents_.size() - offset; + } + memcpy(scratch, &contents_[offset], n); + *result = Slice(scratch, n); + return Status::OK(); + } + + private: + std::string contents_; +}; + +typedef std::map KVMap; + +// Helper class for tests to unify the interface between +// BlockBuilder/TableBuilder and Block/Table. +class Constructor { + public: + explicit Constructor(const Comparator* cmp) : data_(STLLessThan(cmp)) { } + virtual ~Constructor() { } + + void Add(const std::string& key, const Slice& value) { + data_[key] = value.ToString(); + } + + // Finish constructing the data structure with all the keys that have + // been added so far. Returns the keys in sorted order in "*keys" + // and stores the key/value pairs in "*kvmap" + void Finish(const Options& options, + std::vector* keys, + KVMap* kvmap) { + *kvmap = data_; + keys->clear(); + for (KVMap::const_iterator it = data_.begin(); + it != data_.end(); + ++it) { + keys->push_back(it->first); + } + data_.clear(); + Status s = FinishImpl(options, *kvmap); + ASSERT_TRUE(s.ok()) << s.ToString(); + } + + // Construct the data structure from the data in "data" + virtual Status FinishImpl(const Options& options, const KVMap& data) = 0; + + virtual Iterator* NewIterator() const = 0; + + virtual const KVMap& data() { return data_; } + + virtual DB* db() const { return NULL; } // Overridden in DBConstructor + + private: + KVMap data_; +}; + +class BlockConstructor: public Constructor { + public: + explicit BlockConstructor(const Comparator* cmp) + : Constructor(cmp), + comparator_(cmp), + block_(NULL) { } + ~BlockConstructor() { + delete block_; + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + delete block_; + block_ = NULL; + BlockBuilder builder(&options); + + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + builder.Add(it->first, it->second); + } + // Open the block + data_ = builder.Finish().ToString(); + BlockContents contents; + contents.data = data_; + contents.cachable = false; + contents.heap_allocated = false; + block_ = new Block(contents); + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return block_->NewIterator(comparator_); + } + + private: + const Comparator* comparator_; + std::string data_; + Block* block_; + + BlockConstructor(); +}; + +class TableConstructor: public Constructor { + public: + TableConstructor(const Comparator* cmp) + : Constructor(cmp), + source_(NULL), table_(NULL) { + } + ~TableConstructor() { + Reset(); + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + Reset(); + StringSink sink; + TableBuilder builder(options, &sink); + + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + builder.Add(it->first, it->second); + ASSERT_TRUE(builder.status().ok()); + } + Status s = builder.Finish(); + ASSERT_TRUE(s.ok()) << s.ToString(); + + ASSERT_EQ(sink.contents().size(), builder.FileSize()); + + // Open the table + source_ = new StringSource(sink.contents()); + Options table_options; + table_options.comparator = options.comparator; + return Table::Open(table_options, source_, sink.contents().size(), &table_); + } + + virtual Iterator* NewIterator() const { + return table_->NewIterator(ReadOptions()); + } + + uint64_t ApproximateOffsetOf(const Slice& key) const { + return table_->ApproximateOffsetOf(key); + } + + private: + void Reset() { + delete table_; + delete source_; + table_ = NULL; + source_ = NULL; + } + + StringSource* source_; + Table* table_; + + TableConstructor(); +}; + +// A helper class that converts internal format keys into user keys +class KeyConvertingIterator: public Iterator { + public: + explicit KeyConvertingIterator(Iterator* iter) : iter_(iter) { } + virtual ~KeyConvertingIterator() { delete iter_; } + virtual bool Valid() const { return iter_->Valid(); } + virtual void Seek(const Slice& target) { + ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue); + std::string encoded; + AppendInternalKey(&encoded, ikey); + iter_->Seek(encoded); + } + virtual void SeekToFirst() { iter_->SeekToFirst(); } + virtual void SeekToLast() { iter_->SeekToLast(); } + virtual void Next() { iter_->Next(); } + virtual void Prev() { iter_->Prev(); } + + virtual Slice key() const { + assert(Valid()); + ParsedInternalKey key; + if (!ParseInternalKey(iter_->key(), &key)) { + status_ = Status::Corruption("malformed internal key"); + return Slice("corrupted key"); + } + return key.user_key; + } + + virtual Slice value() const { return iter_->value(); } + virtual Status status() const { + return status_.ok() ? iter_->status() : status_; + } + + private: + mutable Status status_; + Iterator* iter_; + + // No copying allowed + KeyConvertingIterator(const KeyConvertingIterator&); + void operator=(const KeyConvertingIterator&); +}; + +class MemTableConstructor: public Constructor { + public: + explicit MemTableConstructor(const Comparator* cmp) + : Constructor(cmp), + internal_comparator_(cmp) { + memtable_ = new MemTable(internal_comparator_); + memtable_->Ref(); + } + ~MemTableConstructor() { + memtable_->Unref(); + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + memtable_->Unref(); + memtable_ = new MemTable(internal_comparator_); + memtable_->Ref(); + int seq = 1; + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + memtable_->Add(seq, kTypeValue, it->first, it->second); + seq++; + } + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return new KeyConvertingIterator(memtable_->NewIterator()); + } + + private: + InternalKeyComparator internal_comparator_; + MemTable* memtable_; +}; + +class DBConstructor: public Constructor { + public: + explicit DBConstructor(const Comparator* cmp) + : Constructor(cmp), + comparator_(cmp) { + db_ = NULL; + NewDB(); + } + ~DBConstructor() { + delete db_; + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + delete db_; + db_ = NULL; + NewDB(); + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + WriteBatch batch; + batch.Put(it->first, it->second); + ASSERT_TRUE(db_->Write(WriteOptions(), &batch).ok()); + } + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return db_->NewIterator(ReadOptions()); + } + + virtual DB* db() const { return db_; } + + private: + void NewDB() { + std::string name = test::TmpDir() + "/table_testdb"; + + Options options; + options.comparator = comparator_; + Status status = DestroyDB(name, options); + ASSERT_TRUE(status.ok()) << status.ToString(); + + options.create_if_missing = true; + options.error_if_exists = true; + options.write_buffer_size = 10000; // Something small to force merging + status = DB::Open(options, name, &db_); + ASSERT_TRUE(status.ok()) << status.ToString(); + } + + const Comparator* comparator_; + DB* db_; +}; + +enum TestType { + TABLE_TEST, + BLOCK_TEST, + MEMTABLE_TEST, + DB_TEST +}; + +struct TestArgs { + TestType type; + bool reverse_compare; + int restart_interval; +}; + +static const TestArgs kTestArgList[] = { + { TABLE_TEST, false, 16 }, + { TABLE_TEST, false, 1 }, + { TABLE_TEST, false, 1024 }, + { TABLE_TEST, true, 16 }, + { TABLE_TEST, true, 1 }, + { TABLE_TEST, true, 1024 }, + + { BLOCK_TEST, false, 16 }, + { BLOCK_TEST, false, 1 }, + { BLOCK_TEST, false, 1024 }, + { BLOCK_TEST, true, 16 }, + { BLOCK_TEST, true, 1 }, + { BLOCK_TEST, true, 1024 }, + + // Restart interval does not matter for memtables + { MEMTABLE_TEST, false, 16 }, + { MEMTABLE_TEST, true, 16 }, + + // Do not bother with restart interval variations for DB + { DB_TEST, false, 16 }, + { DB_TEST, true, 16 }, +}; +static const int kNumTestArgs = sizeof(kTestArgList) / sizeof(kTestArgList[0]); + +class Harness { + public: + Harness() : constructor_(NULL) { } + + void Init(const TestArgs& args) { + delete constructor_; + constructor_ = NULL; + options_ = Options(); + + options_.block_restart_interval = args.restart_interval; + // Use shorter block size for tests to exercise block boundary + // conditions more. + options_.block_size = 256; + if (args.reverse_compare) { + options_.comparator = &reverse_key_comparator; + } + switch (args.type) { + case TABLE_TEST: + constructor_ = new TableConstructor(options_.comparator); + break; + case BLOCK_TEST: + constructor_ = new BlockConstructor(options_.comparator); + break; + case MEMTABLE_TEST: + constructor_ = new MemTableConstructor(options_.comparator); + break; + case DB_TEST: + constructor_ = new DBConstructor(options_.comparator); + break; + } + } + + ~Harness() { + delete constructor_; + } + + void Add(const std::string& key, const std::string& value) { + constructor_->Add(key, value); + } + + void Test(Random* rnd) { + std::vector keys; + KVMap data; + constructor_->Finish(options_, &keys, &data); + + TestForwardScan(keys, data); + TestBackwardScan(keys, data); + TestRandomAccess(rnd, keys, data); + } + + void TestForwardScan(const std::vector& keys, + const KVMap& data) { + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + iter->SeekToFirst(); + for (KVMap::const_iterator model_iter = data.begin(); + model_iter != data.end(); + ++model_iter) { + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + iter->Next(); + } + ASSERT_TRUE(!iter->Valid()); + delete iter; + } + + void TestBackwardScan(const std::vector& keys, + const KVMap& data) { + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + iter->SeekToLast(); + for (KVMap::const_reverse_iterator model_iter = data.rbegin(); + model_iter != data.rend(); + ++model_iter) { + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + iter->Prev(); + } + ASSERT_TRUE(!iter->Valid()); + delete iter; + } + + void TestRandomAccess(Random* rnd, + const std::vector& keys, + const KVMap& data) { + static const bool kVerbose = false; + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + KVMap::const_iterator model_iter = data.begin(); + if (kVerbose) fprintf(stderr, "---\n"); + for (int i = 0; i < 200; i++) { + const int toss = rnd->Uniform(5); + switch (toss) { + case 0: { + if (iter->Valid()) { + if (kVerbose) fprintf(stderr, "Next\n"); + iter->Next(); + ++model_iter; + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + } + break; + } + + case 1: { + if (kVerbose) fprintf(stderr, "SeekToFirst\n"); + iter->SeekToFirst(); + model_iter = data.begin(); + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + + case 2: { + std::string key = PickRandomKey(rnd, keys); + model_iter = data.lower_bound(key); + if (kVerbose) fprintf(stderr, "Seek '%s'\n", + EscapeString(key).c_str()); + iter->Seek(Slice(key)); + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + + case 3: { + if (iter->Valid()) { + if (kVerbose) fprintf(stderr, "Prev\n"); + iter->Prev(); + if (model_iter == data.begin()) { + model_iter = data.end(); // Wrap around to invalid value + } else { + --model_iter; + } + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + } + break; + } + + case 4: { + if (kVerbose) fprintf(stderr, "SeekToLast\n"); + iter->SeekToLast(); + if (keys.empty()) { + model_iter = data.end(); + } else { + std::string last = data.rbegin()->first; + model_iter = data.lower_bound(last); + } + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + } + } + delete iter; + } + + std::string ToString(const KVMap& data, const KVMap::const_iterator& it) { + if (it == data.end()) { + return "END"; + } else { + return "'" + it->first + "->" + it->second + "'"; + } + } + + std::string ToString(const KVMap& data, + const KVMap::const_reverse_iterator& it) { + if (it == data.rend()) { + return "END"; + } else { + return "'" + it->first + "->" + it->second + "'"; + } + } + + std::string ToString(const Iterator* it) { + if (!it->Valid()) { + return "END"; + } else { + return "'" + it->key().ToString() + "->" + it->value().ToString() + "'"; + } + } + + std::string PickRandomKey(Random* rnd, const std::vector& keys) { + if (keys.empty()) { + return "foo"; + } else { + const int index = rnd->Uniform(keys.size()); + std::string result = keys[index]; + switch (rnd->Uniform(3)) { + case 0: + // Return an existing key + break; + case 1: { + // Attempt to return something smaller than an existing key + if (result.size() > 0 && result[result.size()-1] > '\0') { + result[result.size()-1]--; + } + break; + } + case 2: { + // Return something larger than an existing key + Increment(options_.comparator, &result); + break; + } + } + return result; + } + } + + // Returns NULL if not running against a DB + DB* db() const { return constructor_->db(); } + + private: + Options options_; + Constructor* constructor_; +}; + +// Test empty table/block. +TEST(Harness, Empty) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 1); + Test(&rnd); + } +} + +// Special test for a block with no restart entries. The C++ leveldb +// code never generates such blocks, but the Java version of leveldb +// seems to. +TEST(Harness, ZeroRestartPointsInBlock) { + char data[sizeof(uint32_t)]; + memset(data, 0, sizeof(data)); + BlockContents contents; + contents.data = Slice(data, sizeof(data)); + contents.cachable = false; + contents.heap_allocated = false; + Block block(contents); + Iterator* iter = block.NewIterator(BytewiseComparator()); + iter->SeekToFirst(); + ASSERT_TRUE(!iter->Valid()); + iter->SeekToLast(); + ASSERT_TRUE(!iter->Valid()); + iter->Seek("foo"); + ASSERT_TRUE(!iter->Valid()); + delete iter; +} + +// Test the empty key +TEST(Harness, SimpleEmptyKey) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 1); + Add("", "v"); + Test(&rnd); + } +} + +TEST(Harness, SimpleSingle) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 2); + Add("abc", "v"); + Test(&rnd); + } +} + +TEST(Harness, SimpleMulti) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 3); + Add("abc", "v"); + Add("abcd", "v"); + Add("ac", "v2"); + Test(&rnd); + } +} + +TEST(Harness, SimpleSpecialKey) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 4); + Add("\xff\xff", "v3"); + Test(&rnd); + } +} + +TEST(Harness, Randomized) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 5); + for (int num_entries = 0; num_entries < 2000; + num_entries += (num_entries < 50 ? 1 : 200)) { + if ((num_entries % 10) == 0) { + fprintf(stderr, "case %d of %d: num_entries = %d\n", + (i + 1), int(kNumTestArgs), num_entries); + } + for (int e = 0; e < num_entries; e++) { + std::string v; + Add(test::RandomKey(&rnd, rnd.Skewed(4)), + test::RandomString(&rnd, rnd.Skewed(5), &v).ToString()); + } + Test(&rnd); + } + } +} + +TEST(Harness, RandomizedLongDB) { + Random rnd(test::RandomSeed()); + TestArgs args = { DB_TEST, false, 16 }; + Init(args); + int num_entries = 100000; + for (int e = 0; e < num_entries; e++) { + std::string v; + Add(test::RandomKey(&rnd, rnd.Skewed(4)), + test::RandomString(&rnd, rnd.Skewed(5), &v).ToString()); + } + Test(&rnd); + + // We must have created enough data to force merging + int files = 0; + for (int level = 0; level < config::kNumLevels; level++) { + std::string value; + char name[100]; + snprintf(name, sizeof(name), "leveldb.num-files-at-level%d", level); + ASSERT_TRUE(db()->GetProperty(name, &value)); + files += atoi(value.c_str()); + } + ASSERT_GT(files, 0); +} + +class MemTableTest { }; + +TEST(MemTableTest, Simple) { + InternalKeyComparator cmp(BytewiseComparator()); + MemTable* memtable = new MemTable(cmp); + memtable->Ref(); + WriteBatch batch; + WriteBatchInternal::SetSequence(&batch, 100); + batch.Put(std::string("k1"), std::string("v1")); + batch.Put(std::string("k2"), std::string("v2")); + batch.Put(std::string("k3"), std::string("v3")); + batch.Put(std::string("largekey"), std::string("vlarge")); + ASSERT_TRUE(WriteBatchInternal::InsertInto(&batch, memtable).ok()); + + Iterator* iter = memtable->NewIterator(); + iter->SeekToFirst(); + while (iter->Valid()) { + fprintf(stderr, "key: '%s' -> '%s'\n", + iter->key().ToString().c_str(), + iter->value().ToString().c_str()); + iter->Next(); + } + + delete iter; + memtable->Unref(); +} + +static bool Between(uint64_t val, uint64_t low, uint64_t high) { + bool result = (val >= low) && (val <= high); + if (!result) { + fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", + (unsigned long long)(val), + (unsigned long long)(low), + (unsigned long long)(high)); + } + return result; +} + +class TableTest { }; + +TEST(TableTest, ApproximateOffsetOfPlain) { + TableConstructor c(BytewiseComparator()); + c.Add("k01", "hello"); + c.Add("k02", "hello2"); + c.Add("k03", std::string(10000, 'x')); + c.Add("k04", std::string(200000, 'x')); + c.Add("k05", std::string(300000, 'x')); + c.Add("k06", "hello3"); + c.Add("k07", std::string(100000, 'x')); + std::vector keys; + KVMap kvmap; + Options options; + options.block_size = 1024; + options.compression = kNoCompression; + c.Finish(options, &keys, &kvmap); + + ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 10000, 11000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04a"), 210000, 211000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"), 210000, 211000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"), 510000, 511000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"), 510000, 511000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 612000)); + +} + +static bool SnappyCompressionSupported() { + std::string out; + Slice in = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + return port::Snappy_Compress(in.data(), in.size(), &out); +} + +TEST(TableTest, ApproximateOffsetOfCompressed) { + if (!SnappyCompressionSupported()) { + fprintf(stderr, "skipping compression tests\n"); + return; + } + + Random rnd(301); + TableConstructor c(BytewiseComparator()); + std::string tmp; + c.Add("k01", "hello"); + c.Add("k02", test::CompressibleString(&rnd, 0.25, 10000, &tmp)); + c.Add("k03", "hello3"); + c.Add("k04", test::CompressibleString(&rnd, 0.25, 10000, &tmp)); + std::vector keys; + KVMap kvmap; + Options options; + options.block_size = 1024; + options.compression = kSnappyCompression; + c.Finish(options, &keys, &kvmap); + + // Expected upper and lower bounds of space used by compressible strings. + static const int kSlop = 1000; // Compressor effectiveness varies. + const int expected = 2500; // 10000 * compression ratio (0.25) + const int min_z = expected - kSlop; + const int max_z = expected + kSlop; + + ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, kSlop)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, kSlop)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, kSlop)); + // Have now emitted a large compressible string, so adjust expected offset. + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), min_z, max_z)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), min_z, max_z)); + // Have now emitted two large compressible strings, so adjust expected offset. + ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 2 * min_z, 2 * max_z)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/two_level_iterator.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/two_level_iterator.cc new file mode 100644 index 0000000..7822eba --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/two_level_iterator.cc @@ -0,0 +1,182 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/two_level_iterator.h" + +#include "leveldb/table.h" +#include "table/block.h" +#include "table/format.h" +#include "table/iterator_wrapper.h" + +namespace leveldb { + +namespace { + +typedef Iterator* (*BlockFunction)(void*, const ReadOptions&, const Slice&); + +class TwoLevelIterator: public Iterator { + public: + TwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options); + + virtual ~TwoLevelIterator(); + + virtual void Seek(const Slice& target); + virtual void SeekToFirst(); + virtual void SeekToLast(); + virtual void Next(); + virtual void Prev(); + + virtual bool Valid() const { + return data_iter_.Valid(); + } + virtual Slice key() const { + assert(Valid()); + return data_iter_.key(); + } + virtual Slice value() const { + assert(Valid()); + return data_iter_.value(); + } + virtual Status status() const { + // It'd be nice if status() returned a const Status& instead of a Status + if (!index_iter_.status().ok()) { + return index_iter_.status(); + } else if (data_iter_.iter() != NULL && !data_iter_.status().ok()) { + return data_iter_.status(); + } else { + return status_; + } + } + + private: + void SaveError(const Status& s) { + if (status_.ok() && !s.ok()) status_ = s; + } + void SkipEmptyDataBlocksForward(); + void SkipEmptyDataBlocksBackward(); + void SetDataIterator(Iterator* data_iter); + void InitDataBlock(); + + BlockFunction block_function_; + void* arg_; + const ReadOptions options_; + Status status_; + IteratorWrapper index_iter_; + IteratorWrapper data_iter_; // May be NULL + // If data_iter_ is non-NULL, then "data_block_handle_" holds the + // "index_value" passed to block_function_ to create the data_iter_. + std::string data_block_handle_; +}; + +TwoLevelIterator::TwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options) + : block_function_(block_function), + arg_(arg), + options_(options), + index_iter_(index_iter), + data_iter_(NULL) { +} + +TwoLevelIterator::~TwoLevelIterator() { +} + +void TwoLevelIterator::Seek(const Slice& target) { + index_iter_.Seek(target); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.Seek(target); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::SeekToFirst() { + index_iter_.SeekToFirst(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToFirst(); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::SeekToLast() { + index_iter_.SeekToLast(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToLast(); + SkipEmptyDataBlocksBackward(); +} + +void TwoLevelIterator::Next() { + assert(Valid()); + data_iter_.Next(); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::Prev() { + assert(Valid()); + data_iter_.Prev(); + SkipEmptyDataBlocksBackward(); +} + + +void TwoLevelIterator::SkipEmptyDataBlocksForward() { + while (data_iter_.iter() == NULL || !data_iter_.Valid()) { + // Move to next block + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + return; + } + index_iter_.Next(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToFirst(); + } +} + +void TwoLevelIterator::SkipEmptyDataBlocksBackward() { + while (data_iter_.iter() == NULL || !data_iter_.Valid()) { + // Move to next block + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + return; + } + index_iter_.Prev(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToLast(); + } +} + +void TwoLevelIterator::SetDataIterator(Iterator* data_iter) { + if (data_iter_.iter() != NULL) SaveError(data_iter_.status()); + data_iter_.Set(data_iter); +} + +void TwoLevelIterator::InitDataBlock() { + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + } else { + Slice handle = index_iter_.value(); + if (data_iter_.iter() != NULL && handle.compare(data_block_handle_) == 0) { + // data_iter_ is already constructed with this iterator, so + // no need to change anything + } else { + Iterator* iter = (*block_function_)(arg_, options_, handle); + data_block_handle_.assign(handle.data(), handle.size()); + SetDataIterator(iter); + } + } +} + +} // namespace + +Iterator* NewTwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options) { + return new TwoLevelIterator(index_iter, block_function, arg, options); +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/two_level_iterator.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/two_level_iterator.h new file mode 100644 index 0000000..629ca34 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/two_level_iterator.h @@ -0,0 +1,34 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ +#define STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ + +#include "leveldb/iterator.h" + +namespace leveldb { + +struct ReadOptions; + +// Return a new two level iterator. A two-level iterator contains an +// index iterator whose values point to a sequence of blocks where +// each block is itself a sequence of key,value pairs. The returned +// two-level iterator yields the concatenation of all key/value pairs +// in the sequence of blocks. Takes ownership of "index_iter" and +// will delete it when no longer needed. +// +// Uses a supplied function to convert an index_iter value into +// an iterator over the contents of the corresponding block. +extern Iterator* NewTwoLevelIterator( + Iterator* index_iter, + Iterator* (*block_function)( + void* arg, + const ReadOptions& options, + const Slice& index_value), + void* arg, + const ReadOptions& options); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/arena.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/arena.cc new file mode 100644 index 0000000..7407821 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/arena.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/arena.h" +#include + +namespace leveldb { + +static const int kBlockSize = 4096; + +Arena::Arena() : memory_usage_(0) { + alloc_ptr_ = NULL; // First allocation will allocate a block + alloc_bytes_remaining_ = 0; +} + +Arena::~Arena() { + for (size_t i = 0; i < blocks_.size(); i++) { + delete[] blocks_[i]; + } +} + +char* Arena::AllocateFallback(size_t bytes) { + if (bytes > kBlockSize / 4) { + // Object is more than a quarter of our block size. Allocate it separately + // to avoid wasting too much space in leftover bytes. + char* result = AllocateNewBlock(bytes); + return result; + } + + // We waste the remaining space in the current block. + alloc_ptr_ = AllocateNewBlock(kBlockSize); + alloc_bytes_remaining_ = kBlockSize; + + char* result = alloc_ptr_; + alloc_ptr_ += bytes; + alloc_bytes_remaining_ -= bytes; + return result; +} + +char* Arena::AllocateAligned(size_t bytes) { + const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8; + assert((align & (align-1)) == 0); // Pointer size should be a power of 2 + size_t current_mod = reinterpret_cast(alloc_ptr_) & (align-1); + size_t slop = (current_mod == 0 ? 0 : align - current_mod); + size_t needed = bytes + slop; + char* result; + if (needed <= alloc_bytes_remaining_) { + result = alloc_ptr_ + slop; + alloc_ptr_ += needed; + alloc_bytes_remaining_ -= needed; + } else { + // AllocateFallback always returned aligned memory + result = AllocateFallback(bytes); + } + assert((reinterpret_cast(result) & (align-1)) == 0); + return result; +} + +char* Arena::AllocateNewBlock(size_t block_bytes) { + char* result = new char[block_bytes]; + blocks_.push_back(result); + memory_usage_.NoBarrier_Store( + reinterpret_cast(MemoryUsage() + block_bytes + sizeof(char*))); + return result; +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/arena.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/arena.h new file mode 100644 index 0000000..48bab33 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/arena.h @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_ARENA_H_ +#define STORAGE_LEVELDB_UTIL_ARENA_H_ + +#include +#include +#include +#include +#include "port/port.h" + +namespace leveldb { + +class Arena { + public: + Arena(); + ~Arena(); + + // Return a pointer to a newly allocated memory block of "bytes" bytes. + char* Allocate(size_t bytes); + + // Allocate memory with the normal alignment guarantees provided by malloc + char* AllocateAligned(size_t bytes); + + // Returns an estimate of the total memory usage of data allocated + // by the arena. + size_t MemoryUsage() const { + return reinterpret_cast(memory_usage_.NoBarrier_Load()); + } + + private: + char* AllocateFallback(size_t bytes); + char* AllocateNewBlock(size_t block_bytes); + + // Allocation state + char* alloc_ptr_; + size_t alloc_bytes_remaining_; + + // Array of new[] allocated memory blocks + std::vector blocks_; + + // Total memory usage of the arena. + port::AtomicPointer memory_usage_; + + // No copying allowed + Arena(const Arena&); + void operator=(const Arena&); +}; + +inline char* Arena::Allocate(size_t bytes) { + // The semantics of what to return are a bit messy if we allow + // 0-byte allocations, so we disallow them here (we don't need + // them for our internal use). + assert(bytes > 0); + if (bytes <= alloc_bytes_remaining_) { + char* result = alloc_ptr_; + alloc_ptr_ += bytes; + alloc_bytes_remaining_ -= bytes; + return result; + } + return AllocateFallback(bytes); +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_ARENA_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/arena_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/arena_test.cc new file mode 100644 index 0000000..58e870e --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/arena_test.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/arena.h" + +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { + +class ArenaTest { }; + +TEST(ArenaTest, Empty) { + Arena arena; +} + +TEST(ArenaTest, Simple) { + std::vector > allocated; + Arena arena; + const int N = 100000; + size_t bytes = 0; + Random rnd(301); + for (int i = 0; i < N; i++) { + size_t s; + if (i % (N / 10) == 0) { + s = i; + } else { + s = rnd.OneIn(4000) ? rnd.Uniform(6000) : + (rnd.OneIn(10) ? rnd.Uniform(100) : rnd.Uniform(20)); + } + if (s == 0) { + // Our arena disallows size 0 allocations. + s = 1; + } + char* r; + if (rnd.OneIn(10)) { + r = arena.AllocateAligned(s); + } else { + r = arena.Allocate(s); + } + + for (size_t b = 0; b < s; b++) { + // Fill the "i"th allocation with a known bit pattern + r[b] = i % 256; + } + bytes += s; + allocated.push_back(std::make_pair(s, r)); + ASSERT_GE(arena.MemoryUsage(), bytes); + if (i > N/10) { + ASSERT_LE(arena.MemoryUsage(), bytes * 1.10); + } + } + for (size_t i = 0; i < allocated.size(); i++) { + size_t num_bytes = allocated[i].first; + const char* p = allocated[i].second; + for (size_t b = 0; b < num_bytes; b++) { + // Check the "i"th allocation for the known bit pattern + ASSERT_EQ(int(p[b]) & 0xff, i % 256); + } + } +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/bloom.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/bloom.cc new file mode 100644 index 0000000..bf3e4ca --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/bloom.cc @@ -0,0 +1,95 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +#include "leveldb/slice.h" +#include "util/hash.h" + +namespace leveldb { + +namespace { +static uint32_t BloomHash(const Slice& key) { + return Hash(key.data(), key.size(), 0xbc9f1d34); +} + +class BloomFilterPolicy : public FilterPolicy { + private: + size_t bits_per_key_; + size_t k_; + + public: + explicit BloomFilterPolicy(int bits_per_key) + : bits_per_key_(bits_per_key) { + // We intentionally round down to reduce probing cost a little bit + k_ = static_cast(bits_per_key * 0.69); // 0.69 =~ ln(2) + if (k_ < 1) k_ = 1; + if (k_ > 30) k_ = 30; + } + + virtual const char* Name() const { + return "leveldb.BuiltinBloomFilter2"; + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + // Compute bloom filter size (in both bits and bytes) + size_t bits = n * bits_per_key_; + + // For small n, we can see a very high false positive rate. Fix it + // by enforcing a minimum bloom filter length. + if (bits < 64) bits = 64; + + size_t bytes = (bits + 7) / 8; + bits = bytes * 8; + + const size_t init_size = dst->size(); + dst->resize(init_size + bytes, 0); + dst->push_back(static_cast(k_)); // Remember # of probes in filter + char* array = &(*dst)[init_size]; + for (int i = 0; i < n; i++) { + // Use double-hashing to generate a sequence of hash values. + // See analysis in [Kirsch,Mitzenmacher 2006]. + uint32_t h = BloomHash(keys[i]); + const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits + for (size_t j = 0; j < k_; j++) { + const uint32_t bitpos = h % bits; + array[bitpos/8] |= (1 << (bitpos % 8)); + h += delta; + } + } + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& bloom_filter) const { + const size_t len = bloom_filter.size(); + if (len < 2) return false; + + const char* array = bloom_filter.data(); + const size_t bits = (len - 1) * 8; + + // Use the encoded k so that we can read filters generated by + // bloom filters created using different parameters. + const size_t k = array[len-1]; + if (k > 30) { + // Reserved for potentially new encodings for short bloom filters. + // Consider it a match. + return true; + } + + uint32_t h = BloomHash(key); + const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits + for (size_t j = 0; j < k; j++) { + const uint32_t bitpos = h % bits; + if ((array[bitpos/8] & (1 << (bitpos % 8))) == 0) return false; + h += delta; + } + return true; + } +}; +} + +const FilterPolicy* NewBloomFilterPolicy(int bits_per_key) { + return new BloomFilterPolicy(bits_per_key); +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/bloom_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/bloom_test.cc new file mode 100644 index 0000000..1b87a2b --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/bloom_test.cc @@ -0,0 +1,162 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +#include "util/coding.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static const int kVerbose = 1; + +static Slice Key(int i, char* buffer) { + EncodeFixed32(buffer, i); + return Slice(buffer, sizeof(uint32_t)); +} + +class BloomTest { + private: + const FilterPolicy* policy_; + std::string filter_; + std::vector keys_; + + public: + BloomTest() : policy_(NewBloomFilterPolicy(10)) { } + + ~BloomTest() { + delete policy_; + } + + void Reset() { + keys_.clear(); + filter_.clear(); + } + + void Add(const Slice& s) { + keys_.push_back(s.ToString()); + } + + void Build() { + std::vector key_slices; + for (size_t i = 0; i < keys_.size(); i++) { + key_slices.push_back(Slice(keys_[i])); + } + filter_.clear(); + policy_->CreateFilter(&key_slices[0], static_cast(key_slices.size()), + &filter_); + keys_.clear(); + if (kVerbose >= 2) DumpFilter(); + } + + size_t FilterSize() const { + return filter_.size(); + } + + void DumpFilter() { + fprintf(stderr, "F("); + for (size_t i = 0; i+1 < filter_.size(); i++) { + const unsigned int c = static_cast(filter_[i]); + for (int j = 0; j < 8; j++) { + fprintf(stderr, "%c", (c & (1 <KeyMayMatch(s, filter_); + } + + double FalsePositiveRate() { + char buffer[sizeof(int)]; + int result = 0; + for (int i = 0; i < 10000; i++) { + if (Matches(Key(i + 1000000000, buffer))) { + result++; + } + } + return result / 10000.0; + } +}; + +TEST(BloomTest, EmptyFilter) { + ASSERT_TRUE(! Matches("hello")); + ASSERT_TRUE(! Matches("world")); +} + +TEST(BloomTest, Small) { + Add("hello"); + Add("world"); + ASSERT_TRUE(Matches("hello")); + ASSERT_TRUE(Matches("world")); + ASSERT_TRUE(! Matches("x")); + ASSERT_TRUE(! Matches("foo")); +} + +static int NextLength(int length) { + if (length < 10) { + length += 1; + } else if (length < 100) { + length += 10; + } else if (length < 1000) { + length += 100; + } else { + length += 1000; + } + return length; +} + +TEST(BloomTest, VaryingLengths) { + char buffer[sizeof(int)]; + + // Count number of filters that significantly exceed the false positive rate + int mediocre_filters = 0; + int good_filters = 0; + + for (int length = 1; length <= 10000; length = NextLength(length)) { + Reset(); + for (int i = 0; i < length; i++) { + Add(Key(i, buffer)); + } + Build(); + + ASSERT_LE(FilterSize(), static_cast((length * 10 / 8) + 40)) + << length; + + // All added keys must match + for (int i = 0; i < length; i++) { + ASSERT_TRUE(Matches(Key(i, buffer))) + << "Length " << length << "; key " << i; + } + + // Check false positive rate + double rate = FalsePositiveRate(); + if (kVerbose >= 1) { + fprintf(stderr, "False positives: %5.2f%% @ length = %6d ; bytes = %6d\n", + rate*100.0, length, static_cast(FilterSize())); + } + ASSERT_LE(rate, 0.02); // Must not be over 2% + if (rate > 0.0125) mediocre_filters++; // Allowed, but not too often + else good_filters++; + } + if (kVerbose >= 1) { + fprintf(stderr, "Filters: %d good, %d mediocre\n", + good_filters, mediocre_filters); + } + ASSERT_LE(mediocre_filters, good_filters/5); +} + +// Different bits-per-byte + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/cache.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/cache.cc new file mode 100644 index 0000000..ce46886 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/cache.cc @@ -0,0 +1,405 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include + +#include "leveldb/cache.h" +#include "port/port.h" +#include "util/hash.h" +#include "util/mutexlock.h" + +namespace leveldb { + +Cache::~Cache() { +} + +namespace { + +// LRU cache implementation +// +// Cache entries have an "in_cache" boolean indicating whether the cache has a +// reference on the entry. The only ways that this can become false without the +// entry being passed to its "deleter" are via Erase(), via Insert() when +// an element with a duplicate key is inserted, or on destruction of the cache. +// +// The cache keeps two linked lists of items in the cache. All items in the +// cache are in one list or the other, and never both. Items still referenced +// by clients but erased from the cache are in neither list. The lists are: +// - in-use: contains the items currently referenced by clients, in no +// particular order. (This list is used for invariant checking. If we +// removed the check, elements that would otherwise be on this list could be +// left as disconnected singleton lists.) +// - LRU: contains the items not currently referenced by clients, in LRU order +// Elements are moved between these lists by the Ref() and Unref() methods, +// when they detect an element in the cache acquiring or losing its only +// external reference. + +// An entry is a variable length heap-allocated structure. Entries +// are kept in a circular doubly linked list ordered by access time. +struct LRUHandle { + void* value; + void (*deleter)(const Slice&, void* value); + LRUHandle* next_hash; + LRUHandle* next; + LRUHandle* prev; + size_t charge; // TODO(opt): Only allow uint32_t? + size_t key_length; + bool in_cache; // Whether entry is in the cache. + uint32_t refs; // References, including cache reference, if present. + uint32_t hash; // Hash of key(); used for fast sharding and comparisons + char key_data[1]; // Beginning of key + + Slice key() const { + // For cheaper lookups, we allow a temporary Handle object + // to store a pointer to a key in "value". + if (next == this) { + return *(reinterpret_cast(value)); + } else { + return Slice(key_data, key_length); + } + } +}; + +// We provide our own simple hash table since it removes a whole bunch +// of porting hacks and is also faster than some of the built-in hash +// table implementations in some of the compiler/runtime combinations +// we have tested. E.g., readrandom speeds up by ~5% over the g++ +// 4.4.3's builtin hashtable. +class HandleTable { + public: + HandleTable() : length_(0), elems_(0), list_(NULL) { Resize(); } + ~HandleTable() { delete[] list_; } + + LRUHandle* Lookup(const Slice& key, uint32_t hash) { + return *FindPointer(key, hash); + } + + LRUHandle* Insert(LRUHandle* h) { + LRUHandle** ptr = FindPointer(h->key(), h->hash); + LRUHandle* old = *ptr; + h->next_hash = (old == NULL ? NULL : old->next_hash); + *ptr = h; + if (old == NULL) { + ++elems_; + if (elems_ > length_) { + // Since each cache entry is fairly large, we aim for a small + // average linked list length (<= 1). + Resize(); + } + } + return old; + } + + LRUHandle* Remove(const Slice& key, uint32_t hash) { + LRUHandle** ptr = FindPointer(key, hash); + LRUHandle* result = *ptr; + if (result != NULL) { + *ptr = result->next_hash; + --elems_; + } + return result; + } + + private: + // The table consists of an array of buckets where each bucket is + // a linked list of cache entries that hash into the bucket. + uint32_t length_; + uint32_t elems_; + LRUHandle** list_; + + // Return a pointer to slot that points to a cache entry that + // matches key/hash. If there is no such cache entry, return a + // pointer to the trailing slot in the corresponding linked list. + LRUHandle** FindPointer(const Slice& key, uint32_t hash) { + LRUHandle** ptr = &list_[hash & (length_ - 1)]; + while (*ptr != NULL && + ((*ptr)->hash != hash || key != (*ptr)->key())) { + ptr = &(*ptr)->next_hash; + } + return ptr; + } + + void Resize() { + uint32_t new_length = 4; + while (new_length < elems_) { + new_length *= 2; + } + LRUHandle** new_list = new LRUHandle*[new_length]; + memset(new_list, 0, sizeof(new_list[0]) * new_length); + uint32_t count = 0; + for (uint32_t i = 0; i < length_; i++) { + LRUHandle* h = list_[i]; + while (h != NULL) { + LRUHandle* next = h->next_hash; + uint32_t hash = h->hash; + LRUHandle** ptr = &new_list[hash & (new_length - 1)]; + h->next_hash = *ptr; + *ptr = h; + h = next; + count++; + } + } + assert(elems_ == count); + delete[] list_; + list_ = new_list; + length_ = new_length; + } +}; + +// A single shard of sharded cache. +class LRUCache { + public: + LRUCache(); + ~LRUCache(); + + // Separate from constructor so caller can easily make an array of LRUCache + void SetCapacity(size_t capacity) { capacity_ = capacity; } + + // Like Cache methods, but with an extra "hash" parameter. + Cache::Handle* Insert(const Slice& key, uint32_t hash, + void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)); + Cache::Handle* Lookup(const Slice& key, uint32_t hash); + void Release(Cache::Handle* handle); + void Erase(const Slice& key, uint32_t hash); + void Prune(); + size_t TotalCharge() const { + MutexLock l(&mutex_); + return usage_; + } + + private: + void LRU_Remove(LRUHandle* e); + void LRU_Append(LRUHandle*list, LRUHandle* e); + void Ref(LRUHandle* e); + void Unref(LRUHandle* e); + bool FinishErase(LRUHandle* e); + + // Initialized before use. + size_t capacity_; + + // mutex_ protects the following state. + mutable port::Mutex mutex_; + size_t usage_; + + // Dummy head of LRU list. + // lru.prev is newest entry, lru.next is oldest entry. + // Entries have refs==1 and in_cache==true. + LRUHandle lru_; + + // Dummy head of in-use list. + // Entries are in use by clients, and have refs >= 2 and in_cache==true. + LRUHandle in_use_; + + HandleTable table_; +}; + +LRUCache::LRUCache() + : usage_(0) { + // Make empty circular linked lists. + lru_.next = &lru_; + lru_.prev = &lru_; + in_use_.next = &in_use_; + in_use_.prev = &in_use_; +} + +LRUCache::~LRUCache() { + assert(in_use_.next == &in_use_); // Error if caller has an unreleased handle + for (LRUHandle* e = lru_.next; e != &lru_; ) { + LRUHandle* next = e->next; + assert(e->in_cache); + e->in_cache = false; + assert(e->refs == 1); // Invariant of lru_ list. + Unref(e); + e = next; + } +} + +void LRUCache::Ref(LRUHandle* e) { + if (e->refs == 1 && e->in_cache) { // If on lru_ list, move to in_use_ list. + LRU_Remove(e); + LRU_Append(&in_use_, e); + } + e->refs++; +} + +void LRUCache::Unref(LRUHandle* e) { + assert(e->refs > 0); + e->refs--; + if (e->refs == 0) { // Deallocate. + assert(!e->in_cache); + (*e->deleter)(e->key(), e->value); + free(e); + } else if (e->in_cache && e->refs == 1) { // No longer in use; move to lru_ list. + LRU_Remove(e); + LRU_Append(&lru_, e); + } +} + +void LRUCache::LRU_Remove(LRUHandle* e) { + e->next->prev = e->prev; + e->prev->next = e->next; +} + +void LRUCache::LRU_Append(LRUHandle* list, LRUHandle* e) { + // Make "e" newest entry by inserting just before *list + e->next = list; + e->prev = list->prev; + e->prev->next = e; + e->next->prev = e; +} + +Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) { + MutexLock l(&mutex_); + LRUHandle* e = table_.Lookup(key, hash); + if (e != NULL) { + Ref(e); + } + return reinterpret_cast(e); +} + +void LRUCache::Release(Cache::Handle* handle) { + MutexLock l(&mutex_); + Unref(reinterpret_cast(handle)); +} + +Cache::Handle* LRUCache::Insert( + const Slice& key, uint32_t hash, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) { + MutexLock l(&mutex_); + + LRUHandle* e = reinterpret_cast( + malloc(sizeof(LRUHandle)-1 + key.size())); + e->value = value; + e->deleter = deleter; + e->charge = charge; + e->key_length = key.size(); + e->hash = hash; + e->in_cache = false; + e->refs = 1; // for the returned handle. + memcpy(e->key_data, key.data(), key.size()); + + if (capacity_ > 0) { + e->refs++; // for the cache's reference. + e->in_cache = true; + LRU_Append(&in_use_, e); + usage_ += charge; + FinishErase(table_.Insert(e)); + } // else don't cache. (Tests use capacity_==0 to turn off caching.) + + while (usage_ > capacity_ && lru_.next != &lru_) { + LRUHandle* old = lru_.next; + assert(old->refs == 1); + bool erased = FinishErase(table_.Remove(old->key(), old->hash)); + if (!erased) { // to avoid unused variable when compiled NDEBUG + assert(erased); + } + } + + return reinterpret_cast(e); +} + +// If e != NULL, finish removing *e from the cache; it has already been removed +// from the hash table. Return whether e != NULL. Requires mutex_ held. +bool LRUCache::FinishErase(LRUHandle* e) { + if (e != NULL) { + assert(e->in_cache); + LRU_Remove(e); + e->in_cache = false; + usage_ -= e->charge; + Unref(e); + } + return e != NULL; +} + +void LRUCache::Erase(const Slice& key, uint32_t hash) { + MutexLock l(&mutex_); + FinishErase(table_.Remove(key, hash)); +} + +void LRUCache::Prune() { + MutexLock l(&mutex_); + while (lru_.next != &lru_) { + LRUHandle* e = lru_.next; + assert(e->refs == 1); + bool erased = FinishErase(table_.Remove(e->key(), e->hash)); + if (!erased) { // to avoid unused variable when compiled NDEBUG + assert(erased); + } + } +} + +static const int kNumShardBits = 4; +static const int kNumShards = 1 << kNumShardBits; + +class ShardedLRUCache : public Cache { + private: + LRUCache shard_[kNumShards]; + port::Mutex id_mutex_; + uint64_t last_id_; + + static inline uint32_t HashSlice(const Slice& s) { + return Hash(s.data(), s.size(), 0); + } + + static uint32_t Shard(uint32_t hash) { + return hash >> (32 - kNumShardBits); + } + + public: + explicit ShardedLRUCache(size_t capacity) + : last_id_(0) { + const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards; + for (int s = 0; s < kNumShards; s++) { + shard_[s].SetCapacity(per_shard); + } + } + virtual ~ShardedLRUCache() { } + virtual Handle* Insert(const Slice& key, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) { + const uint32_t hash = HashSlice(key); + return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter); + } + virtual Handle* Lookup(const Slice& key) { + const uint32_t hash = HashSlice(key); + return shard_[Shard(hash)].Lookup(key, hash); + } + virtual void Release(Handle* handle) { + LRUHandle* h = reinterpret_cast(handle); + shard_[Shard(h->hash)].Release(handle); + } + virtual void Erase(const Slice& key) { + const uint32_t hash = HashSlice(key); + shard_[Shard(hash)].Erase(key, hash); + } + virtual void* Value(Handle* handle) { + return reinterpret_cast(handle)->value; + } + virtual uint64_t NewId() { + MutexLock l(&id_mutex_); + return ++(last_id_); + } + virtual void Prune() { + for (int s = 0; s < kNumShards; s++) { + shard_[s].Prune(); + } + } + virtual size_t TotalCharge() const { + size_t total = 0; + for (int s = 0; s < kNumShards; s++) { + total += shard_[s].TotalCharge(); + } + return total; + } +}; + +} // end anonymous namespace + +Cache* NewLRUCache(size_t capacity) { + return new ShardedLRUCache(capacity); +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/cache_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/cache_test.cc new file mode 100644 index 0000000..468f7a6 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/cache_test.cc @@ -0,0 +1,226 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/cache.h" + +#include +#include "util/coding.h" +#include "util/testharness.h" + +namespace leveldb { + +// Conversions between numeric keys/values and the types expected by Cache. +static std::string EncodeKey(int k) { + std::string result; + PutFixed32(&result, k); + return result; +} +static int DecodeKey(const Slice& k) { + assert(k.size() == 4); + return DecodeFixed32(k.data()); +} +static void* EncodeValue(uintptr_t v) { return reinterpret_cast(v); } +static int DecodeValue(void* v) { return reinterpret_cast(v); } + +class CacheTest { + public: + static CacheTest* current_; + + static void Deleter(const Slice& key, void* v) { + current_->deleted_keys_.push_back(DecodeKey(key)); + current_->deleted_values_.push_back(DecodeValue(v)); + } + + static const int kCacheSize = 1000; + std::vector deleted_keys_; + std::vector deleted_values_; + Cache* cache_; + + CacheTest() : cache_(NewLRUCache(kCacheSize)) { + current_ = this; + } + + ~CacheTest() { + delete cache_; + } + + int Lookup(int key) { + Cache::Handle* handle = cache_->Lookup(EncodeKey(key)); + const int r = (handle == NULL) ? -1 : DecodeValue(cache_->Value(handle)); + if (handle != NULL) { + cache_->Release(handle); + } + return r; + } + + void Insert(int key, int value, int charge = 1) { + cache_->Release(cache_->Insert(EncodeKey(key), EncodeValue(value), charge, + &CacheTest::Deleter)); + } + + Cache::Handle* InsertAndReturnHandle(int key, int value, int charge = 1) { + return cache_->Insert(EncodeKey(key), EncodeValue(value), charge, + &CacheTest::Deleter); + } + + void Erase(int key) { + cache_->Erase(EncodeKey(key)); + } +}; +CacheTest* CacheTest::current_; + +TEST(CacheTest, HitAndMiss) { + ASSERT_EQ(-1, Lookup(100)); + + Insert(100, 101); + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(-1, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + Insert(200, 201); + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + Insert(100, 102); + ASSERT_EQ(102, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); +} + +TEST(CacheTest, Erase) { + Erase(200); + ASSERT_EQ(0, deleted_keys_.size()); + + Insert(100, 101); + Insert(200, 201); + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); + + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(1, deleted_keys_.size()); +} + +TEST(CacheTest, EntriesArePinned) { + Insert(100, 101); + Cache::Handle* h1 = cache_->Lookup(EncodeKey(100)); + ASSERT_EQ(101, DecodeValue(cache_->Value(h1))); + + Insert(100, 102); + Cache::Handle* h2 = cache_->Lookup(EncodeKey(100)); + ASSERT_EQ(102, DecodeValue(cache_->Value(h2))); + ASSERT_EQ(0, deleted_keys_.size()); + + cache_->Release(h1); + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); + + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(1, deleted_keys_.size()); + + cache_->Release(h2); + ASSERT_EQ(2, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[1]); + ASSERT_EQ(102, deleted_values_[1]); +} + +TEST(CacheTest, EvictionPolicy) { + Insert(100, 101); + Insert(200, 201); + Insert(300, 301); + Cache::Handle* h = cache_->Lookup(EncodeKey(300)); + + // Frequently used entry must be kept around, + // as must things that are still in use. + for (int i = 0; i < kCacheSize + 100; i++) { + Insert(1000+i, 2000+i); + ASSERT_EQ(2000+i, Lookup(1000+i)); + ASSERT_EQ(101, Lookup(100)); + } + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(-1, Lookup(200)); + ASSERT_EQ(301, Lookup(300)); + cache_->Release(h); +} + +TEST(CacheTest, UseExceedsCacheSize) { + // Overfill the cache, keeping handles on all inserted entries. + std::vector h; + for (int i = 0; i < kCacheSize + 100; i++) { + h.push_back(InsertAndReturnHandle(1000+i, 2000+i)); + } + + // Check that all the entries can be found in the cache. + for (int i = 0; i < h.size(); i++) { + ASSERT_EQ(2000+i, Lookup(1000+i)); + } + + for (int i = 0; i < h.size(); i++) { + cache_->Release(h[i]); + } +} + +TEST(CacheTest, HeavyEntries) { + // Add a bunch of light and heavy entries and then count the combined + // size of items still in the cache, which must be approximately the + // same as the total capacity. + const int kLight = 1; + const int kHeavy = 10; + int added = 0; + int index = 0; + while (added < 2*kCacheSize) { + const int weight = (index & 1) ? kLight : kHeavy; + Insert(index, 1000+index, weight); + added += weight; + index++; + } + + int cached_weight = 0; + for (int i = 0; i < index; i++) { + const int weight = (i & 1 ? kLight : kHeavy); + int r = Lookup(i); + if (r >= 0) { + cached_weight += weight; + ASSERT_EQ(1000+i, r); + } + } + ASSERT_LE(cached_weight, kCacheSize + kCacheSize/10); +} + +TEST(CacheTest, NewId) { + uint64_t a = cache_->NewId(); + uint64_t b = cache_->NewId(); + ASSERT_NE(a, b); +} + +TEST(CacheTest, Prune) { + Insert(1, 100); + Insert(2, 200); + + Cache::Handle* handle = cache_->Lookup(EncodeKey(1)); + ASSERT_TRUE(handle); + cache_->Prune(); + cache_->Release(handle); + + ASSERT_EQ(100, Lookup(1)); + ASSERT_EQ(-1, Lookup(2)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/coding.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/coding.cc new file mode 100644 index 0000000..21e3186 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/coding.cc @@ -0,0 +1,194 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/coding.h" + +namespace leveldb { + +void EncodeFixed32(char* buf, uint32_t value) { + if (port::kLittleEndian) { + memcpy(buf, &value, sizeof(value)); + } else { + buf[0] = value & 0xff; + buf[1] = (value >> 8) & 0xff; + buf[2] = (value >> 16) & 0xff; + buf[3] = (value >> 24) & 0xff; + } +} + +void EncodeFixed64(char* buf, uint64_t value) { + if (port::kLittleEndian) { + memcpy(buf, &value, sizeof(value)); + } else { + buf[0] = value & 0xff; + buf[1] = (value >> 8) & 0xff; + buf[2] = (value >> 16) & 0xff; + buf[3] = (value >> 24) & 0xff; + buf[4] = (value >> 32) & 0xff; + buf[5] = (value >> 40) & 0xff; + buf[6] = (value >> 48) & 0xff; + buf[7] = (value >> 56) & 0xff; + } +} + +void PutFixed32(std::string* dst, uint32_t value) { + char buf[sizeof(value)]; + EncodeFixed32(buf, value); + dst->append(buf, sizeof(buf)); +} + +void PutFixed64(std::string* dst, uint64_t value) { + char buf[sizeof(value)]; + EncodeFixed64(buf, value); + dst->append(buf, sizeof(buf)); +} + +char* EncodeVarint32(char* dst, uint32_t v) { + // Operate on characters as unsigneds + unsigned char* ptr = reinterpret_cast(dst); + static const int B = 128; + if (v < (1<<7)) { + *(ptr++) = v; + } else if (v < (1<<14)) { + *(ptr++) = v | B; + *(ptr++) = v>>7; + } else if (v < (1<<21)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = v>>14; + } else if (v < (1<<28)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = v>>21; + } else { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = (v>>21) | B; + *(ptr++) = v>>28; + } + return reinterpret_cast(ptr); +} + +void PutVarint32(std::string* dst, uint32_t v) { + char buf[5]; + char* ptr = EncodeVarint32(buf, v); + dst->append(buf, ptr - buf); +} + +char* EncodeVarint64(char* dst, uint64_t v) { + static const int B = 128; + unsigned char* ptr = reinterpret_cast(dst); + while (v >= B) { + *(ptr++) = (v & (B-1)) | B; + v >>= 7; + } + *(ptr++) = static_cast(v); + return reinterpret_cast(ptr); +} + +void PutVarint64(std::string* dst, uint64_t v) { + char buf[10]; + char* ptr = EncodeVarint64(buf, v); + dst->append(buf, ptr - buf); +} + +void PutLengthPrefixedSlice(std::string* dst, const Slice& value) { + PutVarint32(dst, value.size()); + dst->append(value.data(), value.size()); +} + +int VarintLength(uint64_t v) { + int len = 1; + while (v >= 128) { + v >>= 7; + len++; + } + return len; +} + +const char* GetVarint32PtrFallback(const char* p, + const char* limit, + uint32_t* value) { + uint32_t result = 0; + for (uint32_t shift = 0; shift <= 28 && p < limit; shift += 7) { + uint32_t byte = *(reinterpret_cast(p)); + p++; + if (byte & 128) { + // More bytes are present + result |= ((byte & 127) << shift); + } else { + result |= (byte << shift); + *value = result; + return reinterpret_cast(p); + } + } + return NULL; +} + +bool GetVarint32(Slice* input, uint32_t* value) { + const char* p = input->data(); + const char* limit = p + input->size(); + const char* q = GetVarint32Ptr(p, limit, value); + if (q == NULL) { + return false; + } else { + *input = Slice(q, limit - q); + return true; + } +} + +const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* value) { + uint64_t result = 0; + for (uint32_t shift = 0; shift <= 63 && p < limit; shift += 7) { + uint64_t byte = *(reinterpret_cast(p)); + p++; + if (byte & 128) { + // More bytes are present + result |= ((byte & 127) << shift); + } else { + result |= (byte << shift); + *value = result; + return reinterpret_cast(p); + } + } + return NULL; +} + +bool GetVarint64(Slice* input, uint64_t* value) { + const char* p = input->data(); + const char* limit = p + input->size(); + const char* q = GetVarint64Ptr(p, limit, value); + if (q == NULL) { + return false; + } else { + *input = Slice(q, limit - q); + return true; + } +} + +const char* GetLengthPrefixedSlice(const char* p, const char* limit, + Slice* result) { + uint32_t len; + p = GetVarint32Ptr(p, limit, &len); + if (p == NULL) return NULL; + if (p + len > limit) return NULL; + *result = Slice(p, len); + return p + len; +} + +bool GetLengthPrefixedSlice(Slice* input, Slice* result) { + uint32_t len; + if (GetVarint32(input, &len) && + input->size() >= len) { + *result = Slice(input->data(), len); + input->remove_prefix(len); + return true; + } else { + return false; + } +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/coding.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/coding.h new file mode 100644 index 0000000..3993c4a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/coding.h @@ -0,0 +1,104 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Endian-neutral encoding: +// * Fixed-length numbers are encoded with least-significant byte first +// * In addition we support variable length "varint" encoding +// * Strings are encoded prefixed by their length in varint format + +#ifndef STORAGE_LEVELDB_UTIL_CODING_H_ +#define STORAGE_LEVELDB_UTIL_CODING_H_ + +#include +#include +#include +#include "leveldb/slice.h" +#include "port/port.h" + +namespace leveldb { + +// Standard Put... routines append to a string +extern void PutFixed32(std::string* dst, uint32_t value); +extern void PutFixed64(std::string* dst, uint64_t value); +extern void PutVarint32(std::string* dst, uint32_t value); +extern void PutVarint64(std::string* dst, uint64_t value); +extern void PutLengthPrefixedSlice(std::string* dst, const Slice& value); + +// Standard Get... routines parse a value from the beginning of a Slice +// and advance the slice past the parsed value. +extern bool GetVarint32(Slice* input, uint32_t* value); +extern bool GetVarint64(Slice* input, uint64_t* value); +extern bool GetLengthPrefixedSlice(Slice* input, Slice* result); + +// Pointer-based variants of GetVarint... These either store a value +// in *v and return a pointer just past the parsed value, or return +// NULL on error. These routines only look at bytes in the range +// [p..limit-1] +extern const char* GetVarint32Ptr(const char* p,const char* limit, uint32_t* v); +extern const char* GetVarint64Ptr(const char* p,const char* limit, uint64_t* v); + +// Returns the length of the varint32 or varint64 encoding of "v" +extern int VarintLength(uint64_t v); + +// Lower-level versions of Put... that write directly into a character buffer +// REQUIRES: dst has enough space for the value being written +extern void EncodeFixed32(char* dst, uint32_t value); +extern void EncodeFixed64(char* dst, uint64_t value); + +// Lower-level versions of Put... that write directly into a character buffer +// and return a pointer just past the last byte written. +// REQUIRES: dst has enough space for the value being written +extern char* EncodeVarint32(char* dst, uint32_t value); +extern char* EncodeVarint64(char* dst, uint64_t value); + +// Lower-level versions of Get... that read directly from a character buffer +// without any bounds checking. + +inline uint32_t DecodeFixed32(const char* ptr) { + if (port::kLittleEndian) { + // Load the raw bytes + uint32_t result; + memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load + return result; + } else { + return ((static_cast(static_cast(ptr[0]))) + | (static_cast(static_cast(ptr[1])) << 8) + | (static_cast(static_cast(ptr[2])) << 16) + | (static_cast(static_cast(ptr[3])) << 24)); + } +} + +inline uint64_t DecodeFixed64(const char* ptr) { + if (port::kLittleEndian) { + // Load the raw bytes + uint64_t result; + memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load + return result; + } else { + uint64_t lo = DecodeFixed32(ptr); + uint64_t hi = DecodeFixed32(ptr + 4); + return (hi << 32) | lo; + } +} + +// Internal routine for use by fallback path of GetVarint32Ptr +extern const char* GetVarint32PtrFallback(const char* p, + const char* limit, + uint32_t* value); +inline const char* GetVarint32Ptr(const char* p, + const char* limit, + uint32_t* value) { + if (p < limit) { + uint32_t result = *(reinterpret_cast(p)); + if ((result & 128) == 0) { + *value = result; + return p + 1; + } + } + return GetVarint32PtrFallback(p, limit, value); +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_CODING_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/coding_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/coding_test.cc new file mode 100644 index 0000000..521541e --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/coding_test.cc @@ -0,0 +1,196 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/coding.h" + +#include "util/testharness.h" + +namespace leveldb { + +class Coding { }; + +TEST(Coding, Fixed32) { + std::string s; + for (uint32_t v = 0; v < 100000; v++) { + PutFixed32(&s, v); + } + + const char* p = s.data(); + for (uint32_t v = 0; v < 100000; v++) { + uint32_t actual = DecodeFixed32(p); + ASSERT_EQ(v, actual); + p += sizeof(uint32_t); + } +} + +TEST(Coding, Fixed64) { + std::string s; + for (int power = 0; power <= 63; power++) { + uint64_t v = static_cast(1) << power; + PutFixed64(&s, v - 1); + PutFixed64(&s, v + 0); + PutFixed64(&s, v + 1); + } + + const char* p = s.data(); + for (int power = 0; power <= 63; power++) { + uint64_t v = static_cast(1) << power; + uint64_t actual; + actual = DecodeFixed64(p); + ASSERT_EQ(v-1, actual); + p += sizeof(uint64_t); + + actual = DecodeFixed64(p); + ASSERT_EQ(v+0, actual); + p += sizeof(uint64_t); + + actual = DecodeFixed64(p); + ASSERT_EQ(v+1, actual); + p += sizeof(uint64_t); + } +} + +// Test that encoding routines generate little-endian encodings +TEST(Coding, EncodingOutput) { + std::string dst; + PutFixed32(&dst, 0x04030201); + ASSERT_EQ(4, dst.size()); + ASSERT_EQ(0x01, static_cast(dst[0])); + ASSERT_EQ(0x02, static_cast(dst[1])); + ASSERT_EQ(0x03, static_cast(dst[2])); + ASSERT_EQ(0x04, static_cast(dst[3])); + + dst.clear(); + PutFixed64(&dst, 0x0807060504030201ull); + ASSERT_EQ(8, dst.size()); + ASSERT_EQ(0x01, static_cast(dst[0])); + ASSERT_EQ(0x02, static_cast(dst[1])); + ASSERT_EQ(0x03, static_cast(dst[2])); + ASSERT_EQ(0x04, static_cast(dst[3])); + ASSERT_EQ(0x05, static_cast(dst[4])); + ASSERT_EQ(0x06, static_cast(dst[5])); + ASSERT_EQ(0x07, static_cast(dst[6])); + ASSERT_EQ(0x08, static_cast(dst[7])); +} + +TEST(Coding, Varint32) { + std::string s; + for (uint32_t i = 0; i < (32 * 32); i++) { + uint32_t v = (i / 32) << (i % 32); + PutVarint32(&s, v); + } + + const char* p = s.data(); + const char* limit = p + s.size(); + for (uint32_t i = 0; i < (32 * 32); i++) { + uint32_t expected = (i / 32) << (i % 32); + uint32_t actual; + const char* start = p; + p = GetVarint32Ptr(p, limit, &actual); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(expected, actual); + ASSERT_EQ(VarintLength(actual), p - start); + } + ASSERT_EQ(p, s.data() + s.size()); +} + +TEST(Coding, Varint64) { + // Construct the list of values to check + std::vector values; + // Some special values + values.push_back(0); + values.push_back(100); + values.push_back(~static_cast(0)); + values.push_back(~static_cast(0) - 1); + for (uint32_t k = 0; k < 64; k++) { + // Test values near powers of two + const uint64_t power = 1ull << k; + values.push_back(power); + values.push_back(power-1); + values.push_back(power+1); + } + + std::string s; + for (size_t i = 0; i < values.size(); i++) { + PutVarint64(&s, values[i]); + } + + const char* p = s.data(); + const char* limit = p + s.size(); + for (size_t i = 0; i < values.size(); i++) { + ASSERT_TRUE(p < limit); + uint64_t actual; + const char* start = p; + p = GetVarint64Ptr(p, limit, &actual); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(values[i], actual); + ASSERT_EQ(VarintLength(actual), p - start); + } + ASSERT_EQ(p, limit); + +} + +TEST(Coding, Varint32Overflow) { + uint32_t result; + std::string input("\x81\x82\x83\x84\x85\x11"); + ASSERT_TRUE(GetVarint32Ptr(input.data(), input.data() + input.size(), &result) + == NULL); +} + +TEST(Coding, Varint32Truncation) { + uint32_t large_value = (1u << 31) + 100; + std::string s; + PutVarint32(&s, large_value); + uint32_t result; + for (size_t len = 0; len < s.size() - 1; len++) { + ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + len, &result) == NULL); + } + ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + s.size(), &result) != NULL); + ASSERT_EQ(large_value, result); +} + +TEST(Coding, Varint64Overflow) { + uint64_t result; + std::string input("\x81\x82\x83\x84\x85\x81\x82\x83\x84\x85\x11"); + ASSERT_TRUE(GetVarint64Ptr(input.data(), input.data() + input.size(), &result) + == NULL); +} + +TEST(Coding, Varint64Truncation) { + uint64_t large_value = (1ull << 63) + 100ull; + std::string s; + PutVarint64(&s, large_value); + uint64_t result; + for (size_t len = 0; len < s.size() - 1; len++) { + ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + len, &result) == NULL); + } + ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + s.size(), &result) != NULL); + ASSERT_EQ(large_value, result); +} + +TEST(Coding, Strings) { + std::string s; + PutLengthPrefixedSlice(&s, Slice("")); + PutLengthPrefixedSlice(&s, Slice("foo")); + PutLengthPrefixedSlice(&s, Slice("bar")); + PutLengthPrefixedSlice(&s, Slice(std::string(200, 'x'))); + + Slice input(s); + Slice v; + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("foo", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("bar", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ(std::string(200, 'x'), v.ToString()); + ASSERT_EQ("", input.ToString()); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/comparator.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/comparator.cc new file mode 100644 index 0000000..4b7b572 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/comparator.cc @@ -0,0 +1,81 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include "leveldb/comparator.h" +#include "leveldb/slice.h" +#include "port/port.h" +#include "util/logging.h" + +namespace leveldb { + +Comparator::~Comparator() { } + +namespace { +class BytewiseComparatorImpl : public Comparator { + public: + BytewiseComparatorImpl() { } + + virtual const char* Name() const { + return "leveldb.BytewiseComparator"; + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return a.compare(b); + } + + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const { + // Find length of common prefix + size_t min_length = std::min(start->size(), limit.size()); + size_t diff_index = 0; + while ((diff_index < min_length) && + ((*start)[diff_index] == limit[diff_index])) { + diff_index++; + } + + if (diff_index >= min_length) { + // Do not shorten if one string is a prefix of the other + } else { + uint8_t diff_byte = static_cast((*start)[diff_index]); + if (diff_byte < static_cast(0xff) && + diff_byte + 1 < static_cast(limit[diff_index])) { + (*start)[diff_index]++; + start->resize(diff_index + 1); + assert(Compare(*start, limit) < 0); + } + } + } + + virtual void FindShortSuccessor(std::string* key) const { + // Find first character that can be incremented + size_t n = key->size(); + for (size_t i = 0; i < n; i++) { + const uint8_t byte = (*key)[i]; + if (byte != static_cast(0xff)) { + (*key)[i] = byte + 1; + key->resize(i+1); + return; + } + } + // *key is a run of 0xffs. Leave it alone. + } +}; +} // namespace + +static port::OnceType once = LEVELDB_ONCE_INIT; +static const Comparator* bytewise; + +static void InitModule() { + bytewise = new BytewiseComparatorImpl; +} + +const Comparator* BytewiseComparator() { + port::InitOnce(&once, InitModule); + return bytewise; +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/crc32c.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/crc32c.cc new file mode 100644 index 0000000..edd61cf --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/crc32c.cc @@ -0,0 +1,350 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A portable implementation of crc32c, optimized to handle +// four bytes at a time. + +#include "util/crc32c.h" + +#include + +#include "port/port.h" +#include "util/coding.h" + +namespace leveldb { +namespace crc32c { + +static const uint32_t table0_[256] = { + 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, + 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb, + 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, + 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, + 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, + 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, + 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, + 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b, + 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, + 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, + 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, + 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, + 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, + 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a, + 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, + 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, + 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, + 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, + 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, + 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198, + 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, + 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, + 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, + 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, + 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, + 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789, + 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, + 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, + 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, + 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, + 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, + 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829, + 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, + 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, + 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, + 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, + 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, + 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc, + 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, + 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, + 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, + 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, + 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, + 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982, + 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, + 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, + 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, + 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, + 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, + 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f, + 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, + 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, + 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, + 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, + 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, + 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f, + 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, + 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, + 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, + 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, + 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, + 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e, + 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, + 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 +}; +static const uint32_t table1_[256] = { + 0x00000000, 0x13a29877, 0x274530ee, 0x34e7a899, + 0x4e8a61dc, 0x5d28f9ab, 0x69cf5132, 0x7a6dc945, + 0x9d14c3b8, 0x8eb65bcf, 0xba51f356, 0xa9f36b21, + 0xd39ea264, 0xc03c3a13, 0xf4db928a, 0xe7790afd, + 0x3fc5f181, 0x2c6769f6, 0x1880c16f, 0x0b225918, + 0x714f905d, 0x62ed082a, 0x560aa0b3, 0x45a838c4, + 0xa2d13239, 0xb173aa4e, 0x859402d7, 0x96369aa0, + 0xec5b53e5, 0xfff9cb92, 0xcb1e630b, 0xd8bcfb7c, + 0x7f8be302, 0x6c297b75, 0x58ced3ec, 0x4b6c4b9b, + 0x310182de, 0x22a31aa9, 0x1644b230, 0x05e62a47, + 0xe29f20ba, 0xf13db8cd, 0xc5da1054, 0xd6788823, + 0xac154166, 0xbfb7d911, 0x8b507188, 0x98f2e9ff, + 0x404e1283, 0x53ec8af4, 0x670b226d, 0x74a9ba1a, + 0x0ec4735f, 0x1d66eb28, 0x298143b1, 0x3a23dbc6, + 0xdd5ad13b, 0xcef8494c, 0xfa1fe1d5, 0xe9bd79a2, + 0x93d0b0e7, 0x80722890, 0xb4958009, 0xa737187e, + 0xff17c604, 0xecb55e73, 0xd852f6ea, 0xcbf06e9d, + 0xb19da7d8, 0xa23f3faf, 0x96d89736, 0x857a0f41, + 0x620305bc, 0x71a19dcb, 0x45463552, 0x56e4ad25, + 0x2c896460, 0x3f2bfc17, 0x0bcc548e, 0x186eccf9, + 0xc0d23785, 0xd370aff2, 0xe797076b, 0xf4359f1c, + 0x8e585659, 0x9dface2e, 0xa91d66b7, 0xbabffec0, + 0x5dc6f43d, 0x4e646c4a, 0x7a83c4d3, 0x69215ca4, + 0x134c95e1, 0x00ee0d96, 0x3409a50f, 0x27ab3d78, + 0x809c2506, 0x933ebd71, 0xa7d915e8, 0xb47b8d9f, + 0xce1644da, 0xddb4dcad, 0xe9537434, 0xfaf1ec43, + 0x1d88e6be, 0x0e2a7ec9, 0x3acdd650, 0x296f4e27, + 0x53028762, 0x40a01f15, 0x7447b78c, 0x67e52ffb, + 0xbf59d487, 0xacfb4cf0, 0x981ce469, 0x8bbe7c1e, + 0xf1d3b55b, 0xe2712d2c, 0xd69685b5, 0xc5341dc2, + 0x224d173f, 0x31ef8f48, 0x050827d1, 0x16aabfa6, + 0x6cc776e3, 0x7f65ee94, 0x4b82460d, 0x5820de7a, + 0xfbc3faf9, 0xe861628e, 0xdc86ca17, 0xcf245260, + 0xb5499b25, 0xa6eb0352, 0x920cabcb, 0x81ae33bc, + 0x66d73941, 0x7575a136, 0x419209af, 0x523091d8, + 0x285d589d, 0x3bffc0ea, 0x0f186873, 0x1cbaf004, + 0xc4060b78, 0xd7a4930f, 0xe3433b96, 0xf0e1a3e1, + 0x8a8c6aa4, 0x992ef2d3, 0xadc95a4a, 0xbe6bc23d, + 0x5912c8c0, 0x4ab050b7, 0x7e57f82e, 0x6df56059, + 0x1798a91c, 0x043a316b, 0x30dd99f2, 0x237f0185, + 0x844819fb, 0x97ea818c, 0xa30d2915, 0xb0afb162, + 0xcac27827, 0xd960e050, 0xed8748c9, 0xfe25d0be, + 0x195cda43, 0x0afe4234, 0x3e19eaad, 0x2dbb72da, + 0x57d6bb9f, 0x447423e8, 0x70938b71, 0x63311306, + 0xbb8de87a, 0xa82f700d, 0x9cc8d894, 0x8f6a40e3, + 0xf50789a6, 0xe6a511d1, 0xd242b948, 0xc1e0213f, + 0x26992bc2, 0x353bb3b5, 0x01dc1b2c, 0x127e835b, + 0x68134a1e, 0x7bb1d269, 0x4f567af0, 0x5cf4e287, + 0x04d43cfd, 0x1776a48a, 0x23910c13, 0x30339464, + 0x4a5e5d21, 0x59fcc556, 0x6d1b6dcf, 0x7eb9f5b8, + 0x99c0ff45, 0x8a626732, 0xbe85cfab, 0xad2757dc, + 0xd74a9e99, 0xc4e806ee, 0xf00fae77, 0xe3ad3600, + 0x3b11cd7c, 0x28b3550b, 0x1c54fd92, 0x0ff665e5, + 0x759baca0, 0x663934d7, 0x52de9c4e, 0x417c0439, + 0xa6050ec4, 0xb5a796b3, 0x81403e2a, 0x92e2a65d, + 0xe88f6f18, 0xfb2df76f, 0xcfca5ff6, 0xdc68c781, + 0x7b5fdfff, 0x68fd4788, 0x5c1aef11, 0x4fb87766, + 0x35d5be23, 0x26772654, 0x12908ecd, 0x013216ba, + 0xe64b1c47, 0xf5e98430, 0xc10e2ca9, 0xd2acb4de, + 0xa8c17d9b, 0xbb63e5ec, 0x8f844d75, 0x9c26d502, + 0x449a2e7e, 0x5738b609, 0x63df1e90, 0x707d86e7, + 0x0a104fa2, 0x19b2d7d5, 0x2d557f4c, 0x3ef7e73b, + 0xd98eedc6, 0xca2c75b1, 0xfecbdd28, 0xed69455f, + 0x97048c1a, 0x84a6146d, 0xb041bcf4, 0xa3e32483 +}; +static const uint32_t table2_[256] = { + 0x00000000, 0xa541927e, 0x4f6f520d, 0xea2ec073, + 0x9edea41a, 0x3b9f3664, 0xd1b1f617, 0x74f06469, + 0x38513ec5, 0x9d10acbb, 0x773e6cc8, 0xd27ffeb6, + 0xa68f9adf, 0x03ce08a1, 0xe9e0c8d2, 0x4ca15aac, + 0x70a27d8a, 0xd5e3eff4, 0x3fcd2f87, 0x9a8cbdf9, + 0xee7cd990, 0x4b3d4bee, 0xa1138b9d, 0x045219e3, + 0x48f3434f, 0xedb2d131, 0x079c1142, 0xa2dd833c, + 0xd62de755, 0x736c752b, 0x9942b558, 0x3c032726, + 0xe144fb14, 0x4405696a, 0xae2ba919, 0x0b6a3b67, + 0x7f9a5f0e, 0xdadbcd70, 0x30f50d03, 0x95b49f7d, + 0xd915c5d1, 0x7c5457af, 0x967a97dc, 0x333b05a2, + 0x47cb61cb, 0xe28af3b5, 0x08a433c6, 0xade5a1b8, + 0x91e6869e, 0x34a714e0, 0xde89d493, 0x7bc846ed, + 0x0f382284, 0xaa79b0fa, 0x40577089, 0xe516e2f7, + 0xa9b7b85b, 0x0cf62a25, 0xe6d8ea56, 0x43997828, + 0x37691c41, 0x92288e3f, 0x78064e4c, 0xdd47dc32, + 0xc76580d9, 0x622412a7, 0x880ad2d4, 0x2d4b40aa, + 0x59bb24c3, 0xfcfab6bd, 0x16d476ce, 0xb395e4b0, + 0xff34be1c, 0x5a752c62, 0xb05bec11, 0x151a7e6f, + 0x61ea1a06, 0xc4ab8878, 0x2e85480b, 0x8bc4da75, + 0xb7c7fd53, 0x12866f2d, 0xf8a8af5e, 0x5de93d20, + 0x29195949, 0x8c58cb37, 0x66760b44, 0xc337993a, + 0x8f96c396, 0x2ad751e8, 0xc0f9919b, 0x65b803e5, + 0x1148678c, 0xb409f5f2, 0x5e273581, 0xfb66a7ff, + 0x26217bcd, 0x8360e9b3, 0x694e29c0, 0xcc0fbbbe, + 0xb8ffdfd7, 0x1dbe4da9, 0xf7908dda, 0x52d11fa4, + 0x1e704508, 0xbb31d776, 0x511f1705, 0xf45e857b, + 0x80aee112, 0x25ef736c, 0xcfc1b31f, 0x6a802161, + 0x56830647, 0xf3c29439, 0x19ec544a, 0xbcadc634, + 0xc85da25d, 0x6d1c3023, 0x8732f050, 0x2273622e, + 0x6ed23882, 0xcb93aafc, 0x21bd6a8f, 0x84fcf8f1, + 0xf00c9c98, 0x554d0ee6, 0xbf63ce95, 0x1a225ceb, + 0x8b277743, 0x2e66e53d, 0xc448254e, 0x6109b730, + 0x15f9d359, 0xb0b84127, 0x5a968154, 0xffd7132a, + 0xb3764986, 0x1637dbf8, 0xfc191b8b, 0x595889f5, + 0x2da8ed9c, 0x88e97fe2, 0x62c7bf91, 0xc7862def, + 0xfb850ac9, 0x5ec498b7, 0xb4ea58c4, 0x11abcaba, + 0x655baed3, 0xc01a3cad, 0x2a34fcde, 0x8f756ea0, + 0xc3d4340c, 0x6695a672, 0x8cbb6601, 0x29faf47f, + 0x5d0a9016, 0xf84b0268, 0x1265c21b, 0xb7245065, + 0x6a638c57, 0xcf221e29, 0x250cde5a, 0x804d4c24, + 0xf4bd284d, 0x51fcba33, 0xbbd27a40, 0x1e93e83e, + 0x5232b292, 0xf77320ec, 0x1d5de09f, 0xb81c72e1, + 0xccec1688, 0x69ad84f6, 0x83834485, 0x26c2d6fb, + 0x1ac1f1dd, 0xbf8063a3, 0x55aea3d0, 0xf0ef31ae, + 0x841f55c7, 0x215ec7b9, 0xcb7007ca, 0x6e3195b4, + 0x2290cf18, 0x87d15d66, 0x6dff9d15, 0xc8be0f6b, + 0xbc4e6b02, 0x190ff97c, 0xf321390f, 0x5660ab71, + 0x4c42f79a, 0xe90365e4, 0x032da597, 0xa66c37e9, + 0xd29c5380, 0x77ddc1fe, 0x9df3018d, 0x38b293f3, + 0x7413c95f, 0xd1525b21, 0x3b7c9b52, 0x9e3d092c, + 0xeacd6d45, 0x4f8cff3b, 0xa5a23f48, 0x00e3ad36, + 0x3ce08a10, 0x99a1186e, 0x738fd81d, 0xd6ce4a63, + 0xa23e2e0a, 0x077fbc74, 0xed517c07, 0x4810ee79, + 0x04b1b4d5, 0xa1f026ab, 0x4bdee6d8, 0xee9f74a6, + 0x9a6f10cf, 0x3f2e82b1, 0xd50042c2, 0x7041d0bc, + 0xad060c8e, 0x08479ef0, 0xe2695e83, 0x4728ccfd, + 0x33d8a894, 0x96993aea, 0x7cb7fa99, 0xd9f668e7, + 0x9557324b, 0x3016a035, 0xda386046, 0x7f79f238, + 0x0b899651, 0xaec8042f, 0x44e6c45c, 0xe1a75622, + 0xdda47104, 0x78e5e37a, 0x92cb2309, 0x378ab177, + 0x437ad51e, 0xe63b4760, 0x0c158713, 0xa954156d, + 0xe5f54fc1, 0x40b4ddbf, 0xaa9a1dcc, 0x0fdb8fb2, + 0x7b2bebdb, 0xde6a79a5, 0x3444b9d6, 0x91052ba8 +}; +static const uint32_t table3_[256] = { + 0x00000000, 0xdd45aab8, 0xbf672381, 0x62228939, + 0x7b2231f3, 0xa6679b4b, 0xc4451272, 0x1900b8ca, + 0xf64463e6, 0x2b01c95e, 0x49234067, 0x9466eadf, + 0x8d665215, 0x5023f8ad, 0x32017194, 0xef44db2c, + 0xe964b13d, 0x34211b85, 0x560392bc, 0x8b463804, + 0x924680ce, 0x4f032a76, 0x2d21a34f, 0xf06409f7, + 0x1f20d2db, 0xc2657863, 0xa047f15a, 0x7d025be2, + 0x6402e328, 0xb9474990, 0xdb65c0a9, 0x06206a11, + 0xd725148b, 0x0a60be33, 0x6842370a, 0xb5079db2, + 0xac072578, 0x71428fc0, 0x136006f9, 0xce25ac41, + 0x2161776d, 0xfc24ddd5, 0x9e0654ec, 0x4343fe54, + 0x5a43469e, 0x8706ec26, 0xe524651f, 0x3861cfa7, + 0x3e41a5b6, 0xe3040f0e, 0x81268637, 0x5c632c8f, + 0x45639445, 0x98263efd, 0xfa04b7c4, 0x27411d7c, + 0xc805c650, 0x15406ce8, 0x7762e5d1, 0xaa274f69, + 0xb327f7a3, 0x6e625d1b, 0x0c40d422, 0xd1057e9a, + 0xaba65fe7, 0x76e3f55f, 0x14c17c66, 0xc984d6de, + 0xd0846e14, 0x0dc1c4ac, 0x6fe34d95, 0xb2a6e72d, + 0x5de23c01, 0x80a796b9, 0xe2851f80, 0x3fc0b538, + 0x26c00df2, 0xfb85a74a, 0x99a72e73, 0x44e284cb, + 0x42c2eeda, 0x9f874462, 0xfda5cd5b, 0x20e067e3, + 0x39e0df29, 0xe4a57591, 0x8687fca8, 0x5bc25610, + 0xb4868d3c, 0x69c32784, 0x0be1aebd, 0xd6a40405, + 0xcfa4bccf, 0x12e11677, 0x70c39f4e, 0xad8635f6, + 0x7c834b6c, 0xa1c6e1d4, 0xc3e468ed, 0x1ea1c255, + 0x07a17a9f, 0xdae4d027, 0xb8c6591e, 0x6583f3a6, + 0x8ac7288a, 0x57828232, 0x35a00b0b, 0xe8e5a1b3, + 0xf1e51979, 0x2ca0b3c1, 0x4e823af8, 0x93c79040, + 0x95e7fa51, 0x48a250e9, 0x2a80d9d0, 0xf7c57368, + 0xeec5cba2, 0x3380611a, 0x51a2e823, 0x8ce7429b, + 0x63a399b7, 0xbee6330f, 0xdcc4ba36, 0x0181108e, + 0x1881a844, 0xc5c402fc, 0xa7e68bc5, 0x7aa3217d, + 0x52a0c93f, 0x8fe56387, 0xedc7eabe, 0x30824006, + 0x2982f8cc, 0xf4c75274, 0x96e5db4d, 0x4ba071f5, + 0xa4e4aad9, 0x79a10061, 0x1b838958, 0xc6c623e0, + 0xdfc69b2a, 0x02833192, 0x60a1b8ab, 0xbde41213, + 0xbbc47802, 0x6681d2ba, 0x04a35b83, 0xd9e6f13b, + 0xc0e649f1, 0x1da3e349, 0x7f816a70, 0xa2c4c0c8, + 0x4d801be4, 0x90c5b15c, 0xf2e73865, 0x2fa292dd, + 0x36a22a17, 0xebe780af, 0x89c50996, 0x5480a32e, + 0x8585ddb4, 0x58c0770c, 0x3ae2fe35, 0xe7a7548d, + 0xfea7ec47, 0x23e246ff, 0x41c0cfc6, 0x9c85657e, + 0x73c1be52, 0xae8414ea, 0xcca69dd3, 0x11e3376b, + 0x08e38fa1, 0xd5a62519, 0xb784ac20, 0x6ac10698, + 0x6ce16c89, 0xb1a4c631, 0xd3864f08, 0x0ec3e5b0, + 0x17c35d7a, 0xca86f7c2, 0xa8a47efb, 0x75e1d443, + 0x9aa50f6f, 0x47e0a5d7, 0x25c22cee, 0xf8878656, + 0xe1873e9c, 0x3cc29424, 0x5ee01d1d, 0x83a5b7a5, + 0xf90696d8, 0x24433c60, 0x4661b559, 0x9b241fe1, + 0x8224a72b, 0x5f610d93, 0x3d4384aa, 0xe0062e12, + 0x0f42f53e, 0xd2075f86, 0xb025d6bf, 0x6d607c07, + 0x7460c4cd, 0xa9256e75, 0xcb07e74c, 0x16424df4, + 0x106227e5, 0xcd278d5d, 0xaf050464, 0x7240aedc, + 0x6b401616, 0xb605bcae, 0xd4273597, 0x09629f2f, + 0xe6264403, 0x3b63eebb, 0x59416782, 0x8404cd3a, + 0x9d0475f0, 0x4041df48, 0x22635671, 0xff26fcc9, + 0x2e238253, 0xf36628eb, 0x9144a1d2, 0x4c010b6a, + 0x5501b3a0, 0x88441918, 0xea669021, 0x37233a99, + 0xd867e1b5, 0x05224b0d, 0x6700c234, 0xba45688c, + 0xa345d046, 0x7e007afe, 0x1c22f3c7, 0xc167597f, + 0xc747336e, 0x1a0299d6, 0x782010ef, 0xa565ba57, + 0xbc65029d, 0x6120a825, 0x0302211c, 0xde478ba4, + 0x31035088, 0xec46fa30, 0x8e647309, 0x5321d9b1, + 0x4a21617b, 0x9764cbc3, 0xf54642fa, 0x2803e842 +}; + +// Used to fetch a naturally-aligned 32-bit word in little endian byte-order +static inline uint32_t LE_LOAD32(const uint8_t *p) { + return DecodeFixed32(reinterpret_cast(p)); +} + +// Determine if the CPU running this program can accelerate the CRC32C +// calculation. +static bool CanAccelerateCRC32C() { + // port::AcceleretedCRC32C returns zero when unable to accelerate. + static const char kTestCRCBuffer[] = "TestCRCBuffer"; + static const char kBufSize = sizeof(kTestCRCBuffer) - 1; + static const uint32_t kTestCRCValue = 0xdcbc59fa; + + return port::AcceleratedCRC32C(0, kTestCRCBuffer, kBufSize) == kTestCRCValue; +} + +uint32_t Extend(uint32_t crc, const char* buf, size_t size) { + static bool accelerate = CanAccelerateCRC32C(); + if (accelerate) { + return port::AcceleratedCRC32C(crc, buf, size); + } + + const uint8_t *p = reinterpret_cast(buf); + const uint8_t *e = p + size; + uint32_t l = crc ^ 0xffffffffu; + +#define STEP1 do { \ + int c = (l & 0xff) ^ *p++; \ + l = table0_[c] ^ (l >> 8); \ +} while (0) +#define STEP4 do { \ + uint32_t c = l ^ LE_LOAD32(p); \ + p += 4; \ + l = table3_[c & 0xff] ^ \ + table2_[(c >> 8) & 0xff] ^ \ + table1_[(c >> 16) & 0xff] ^ \ + table0_[c >> 24]; \ +} while (0) + + // Point x at first 4-byte aligned byte in string. This might be + // just past the end of the string. + const uintptr_t pval = reinterpret_cast(p); + const uint8_t* x = reinterpret_cast(((pval + 3) >> 2) << 2); + if (x <= e) { + // Process bytes until finished or p is 4-byte aligned + while (p != x) { + STEP1; + } + } + // Process bytes 16 at a time + while ((e-p) >= 16) { + STEP4; STEP4; STEP4; STEP4; + } + // Process bytes 4 at a time + while ((e-p) >= 4) { + STEP4; + } + // Process the last few bytes + while (p != e) { + STEP1; + } +#undef STEP4 +#undef STEP1 + return l ^ 0xffffffffu; +} + +} // namespace crc32c +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/crc32c.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/crc32c.h new file mode 100644 index 0000000..1d7e5c0 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/crc32c.h @@ -0,0 +1,45 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_CRC32C_H_ +#define STORAGE_LEVELDB_UTIL_CRC32C_H_ + +#include +#include + +namespace leveldb { +namespace crc32c { + +// Return the crc32c of concat(A, data[0,n-1]) where init_crc is the +// crc32c of some string A. Extend() is often used to maintain the +// crc32c of a stream of data. +extern uint32_t Extend(uint32_t init_crc, const char* data, size_t n); + +// Return the crc32c of data[0,n-1] +inline uint32_t Value(const char* data, size_t n) { + return Extend(0, data, n); +} + +static const uint32_t kMaskDelta = 0xa282ead8ul; + +// Return a masked representation of crc. +// +// Motivation: it is problematic to compute the CRC of a string that +// contains embedded CRCs. Therefore we recommend that CRCs stored +// somewhere (e.g., in files) should be masked before being stored. +inline uint32_t Mask(uint32_t crc) { + // Rotate right by 15 bits and add a constant. + return ((crc >> 15) | (crc << 17)) + kMaskDelta; +} + +// Return the crc whose masked representation is masked_crc. +inline uint32_t Unmask(uint32_t masked_crc) { + uint32_t rot = masked_crc - kMaskDelta; + return ((rot >> 17) | (rot << 15)); +} + +} // namespace crc32c +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_CRC32C_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/crc32c_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/crc32c_test.cc new file mode 100644 index 0000000..4b957ee --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/crc32c_test.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/crc32c.h" +#include "util/testharness.h" + +namespace leveldb { +namespace crc32c { + +class CRC { }; + +TEST(CRC, StandardResults) { + // From rfc3720 section B.4. + char buf[32]; + + memset(buf, 0, sizeof(buf)); + ASSERT_EQ(0x8a9136aa, Value(buf, sizeof(buf))); + + memset(buf, 0xff, sizeof(buf)); + ASSERT_EQ(0x62a8ab43, Value(buf, sizeof(buf))); + + for (int i = 0; i < 32; i++) { + buf[i] = i; + } + ASSERT_EQ(0x46dd794e, Value(buf, sizeof(buf))); + + for (int i = 0; i < 32; i++) { + buf[i] = 31 - i; + } + ASSERT_EQ(0x113fdb5c, Value(buf, sizeof(buf))); + + unsigned char data[48] = { + 0x01, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x18, + 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + ASSERT_EQ(0xd9963a56, Value(reinterpret_cast(data), sizeof(data))); +} + +TEST(CRC, Values) { + ASSERT_NE(Value("a", 1), Value("foo", 3)); +} + +TEST(CRC, Extend) { + ASSERT_EQ(Value("hello world", 11), + Extend(Value("hello ", 6), "world", 5)); +} + +TEST(CRC, Mask) { + uint32_t crc = Value("foo", 3); + ASSERT_NE(crc, Mask(crc)); + ASSERT_NE(crc, Mask(Mask(crc))); + ASSERT_EQ(crc, Unmask(Mask(crc))); + ASSERT_EQ(crc, Unmask(Unmask(Mask(Mask(crc))))); +} + +} // namespace crc32c +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env.cc new file mode 100644 index 0000000..c58a082 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env.cc @@ -0,0 +1,100 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +namespace leveldb { + +Env::~Env() { +} + +Status Env::NewAppendableFile(const std::string& fname, WritableFile** result) { + return Status::NotSupported("NewAppendableFile", fname); +} + +SequentialFile::~SequentialFile() { +} + +RandomAccessFile::~RandomAccessFile() { +} + +WritableFile::~WritableFile() { +} + +Logger::~Logger() { +} + +FileLock::~FileLock() { +} + +void Log(Logger* info_log, const char* format, ...) { + if (info_log != NULL) { + va_list ap; + va_start(ap, format); + info_log->Logv(format, ap); + va_end(ap); + } +} + +static Status DoWriteStringToFile(Env* env, const Slice& data, + const std::string& fname, + bool should_sync) { + WritableFile* file; + Status s = env->NewWritableFile(fname, &file); + if (!s.ok()) { + return s; + } + s = file->Append(data); + if (s.ok() && should_sync) { + s = file->Sync(); + } + if (s.ok()) { + s = file->Close(); + } + delete file; // Will auto-close if we did not close above + if (!s.ok()) { + env->DeleteFile(fname); + } + return s; +} + +Status WriteStringToFile(Env* env, const Slice& data, + const std::string& fname) { + return DoWriteStringToFile(env, data, fname, false); +} + +Status WriteStringToFileSync(Env* env, const Slice& data, + const std::string& fname) { + return DoWriteStringToFile(env, data, fname, true); +} + +Status ReadFileToString(Env* env, const std::string& fname, std::string* data) { + data->clear(); + SequentialFile* file; + Status s = env->NewSequentialFile(fname, &file); + if (!s.ok()) { + return s; + } + static const int kBufferSize = 8192; + char* space = new char[kBufferSize]; + while (true) { + Slice fragment; + s = file->Read(kBufferSize, &fragment, space); + if (!s.ok()) { + break; + } + data->append(fragment.data(), fragment.size()); + if (fragment.empty()) { + break; + } + } + delete[] space; + delete file; + return s; +} + +EnvWrapper::~EnvWrapper() { +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_posix.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_posix.cc new file mode 100644 index 0000000..84aabb2 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_posix.cc @@ -0,0 +1,695 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/posix_logger.h" +#include "util/env_posix_test_helper.h" + +namespace leveldb { + +namespace { + +static int open_read_only_file_limit = -1; +static int mmap_limit = -1; + +static Status IOError(const std::string& context, int err_number) { + return Status::IOError(context, strerror(err_number)); +} + +// Helper class to limit resource usage to avoid exhaustion. +// Currently used to limit read-only file descriptors and mmap file usage +// so that we do not end up running out of file descriptors, virtual memory, +// or running into kernel performance problems for very large databases. +class Limiter { + public: + // Limit maximum number of resources to |n|. + Limiter(intptr_t n) { + SetAllowed(n); + } + + // If another resource is available, acquire it and return true. + // Else return false. + bool Acquire() { + if (GetAllowed() <= 0) { + return false; + } + MutexLock l(&mu_); + intptr_t x = GetAllowed(); + if (x <= 0) { + return false; + } else { + SetAllowed(x - 1); + return true; + } + } + + // Release a resource acquired by a previous call to Acquire() that returned + // true. + void Release() { + MutexLock l(&mu_); + SetAllowed(GetAllowed() + 1); + } + + private: + port::Mutex mu_; + port::AtomicPointer allowed_; + + intptr_t GetAllowed() const { + return reinterpret_cast(allowed_.Acquire_Load()); + } + + // REQUIRES: mu_ must be held + void SetAllowed(intptr_t v) { + allowed_.Release_Store(reinterpret_cast(v)); + } + + Limiter(const Limiter&); + void operator=(const Limiter&); +}; + +class PosixSequentialFile: public SequentialFile { + private: + std::string filename_; + FILE* file_; + + public: + PosixSequentialFile(const std::string& fname, FILE* f) + : filename_(fname), file_(f) { } + virtual ~PosixSequentialFile() { fclose(file_); } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + Status s; + size_t r = fread_unlocked(scratch, 1, n, file_); + *result = Slice(scratch, r); + if (r < n) { + if (feof(file_)) { + // We leave status as ok if we hit the end of the file + } else { + // A partial read with an error: return a non-ok status + s = IOError(filename_, errno); + } + } + return s; + } + + virtual Status Skip(uint64_t n) { + if (fseek(file_, n, SEEK_CUR)) { + return IOError(filename_, errno); + } + return Status::OK(); + } +}; + +// pread() based random-access +class PosixRandomAccessFile: public RandomAccessFile { + private: + std::string filename_; + bool temporary_fd_; // If true, fd_ is -1 and we open on every read. + int fd_; + Limiter* limiter_; + + public: + PosixRandomAccessFile(const std::string& fname, int fd, Limiter* limiter) + : filename_(fname), fd_(fd), limiter_(limiter) { + temporary_fd_ = !limiter->Acquire(); + if (temporary_fd_) { + // Open file on every access. + close(fd_); + fd_ = -1; + } + } + + virtual ~PosixRandomAccessFile() { + if (!temporary_fd_) { + close(fd_); + limiter_->Release(); + } + } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + int fd = fd_; + if (temporary_fd_) { + fd = open(filename_.c_str(), O_RDONLY); + if (fd < 0) { + return IOError(filename_, errno); + } + } + + Status s; + ssize_t r = pread(fd, scratch, n, static_cast(offset)); + *result = Slice(scratch, (r < 0) ? 0 : r); + if (r < 0) { + // An error: return a non-ok status + s = IOError(filename_, errno); + } + if (temporary_fd_) { + // Close the temporary file descriptor opened earlier. + close(fd); + } + return s; + } +}; + +// mmap() based random-access +class PosixMmapReadableFile: public RandomAccessFile { + private: + std::string filename_; + void* mmapped_region_; + size_t length_; + Limiter* limiter_; + + public: + // base[0,length-1] contains the mmapped contents of the file. + PosixMmapReadableFile(const std::string& fname, void* base, size_t length, + Limiter* limiter) + : filename_(fname), mmapped_region_(base), length_(length), + limiter_(limiter) { + } + + virtual ~PosixMmapReadableFile() { + munmap(mmapped_region_, length_); + limiter_->Release(); + } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + Status s; + if (offset + n > length_) { + *result = Slice(); + s = IOError(filename_, EINVAL); + } else { + *result = Slice(reinterpret_cast(mmapped_region_) + offset, n); + } + return s; + } +}; + +class PosixWritableFile : public WritableFile { + private: + std::string filename_; + FILE* file_; + + public: + PosixWritableFile(const std::string& fname, FILE* f) + : filename_(fname), file_(f) { } + + ~PosixWritableFile() { + if (file_ != NULL) { + // Ignoring any potential errors + fclose(file_); + } + } + + virtual Status Append(const Slice& data) { + size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_); + if (r != data.size()) { + return IOError(filename_, errno); + } + return Status::OK(); + } + + virtual Status Close() { + Status result; + if (fclose(file_) != 0) { + result = IOError(filename_, errno); + } + file_ = NULL; + return result; + } + + virtual Status Flush() { + if (fflush_unlocked(file_) != 0) { + return IOError(filename_, errno); + } + return Status::OK(); + } + + Status SyncDirIfManifest() { + const char* f = filename_.c_str(); + const char* sep = strrchr(f, '/'); + Slice basename; + std::string dir; + if (sep == NULL) { + dir = "."; + basename = f; + } else { + dir = std::string(f, sep - f); + basename = sep + 1; + } + Status s; + if (basename.starts_with("MANIFEST")) { + int fd = open(dir.c_str(), O_RDONLY); + if (fd < 0) { + s = IOError(dir, errno); + } else { + if (fsync(fd) < 0) { + s = IOError(dir, errno); + } + close(fd); + } + } + return s; + } + + virtual Status Sync() { + // Ensure new files referred to by the manifest are in the filesystem. + Status s = SyncDirIfManifest(); + if (!s.ok()) { + return s; + } + if (fflush_unlocked(file_) != 0 || + fdatasync(fileno(file_)) != 0) { + s = Status::IOError(filename_, strerror(errno)); + } + return s; + } +}; + +static int LockOrUnlock(int fd, bool lock) { + errno = 0; + struct flock f; + memset(&f, 0, sizeof(f)); + f.l_type = (lock ? F_WRLCK : F_UNLCK); + f.l_whence = SEEK_SET; + f.l_start = 0; + f.l_len = 0; // Lock/unlock entire file + return fcntl(fd, F_SETLK, &f); +} + +class PosixFileLock : public FileLock { + public: + int fd_; + std::string name_; +}; + +// Set of locked files. We keep a separate set instead of just +// relying on fcntrl(F_SETLK) since fcntl(F_SETLK) does not provide +// any protection against multiple uses from the same process. +class PosixLockTable { + private: + port::Mutex mu_; + std::set locked_files_; + public: + bool Insert(const std::string& fname) { + MutexLock l(&mu_); + return locked_files_.insert(fname).second; + } + void Remove(const std::string& fname) { + MutexLock l(&mu_); + locked_files_.erase(fname); + } +}; + +class PosixEnv : public Env { + public: + PosixEnv(); + virtual ~PosixEnv() { + char msg[] = "Destroying Env::Default()\n"; + fwrite(msg, 1, sizeof(msg), stderr); + abort(); + } + + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) { + FILE* f = fopen(fname.c_str(), "r"); + if (f == NULL) { + *result = NULL; + return IOError(fname, errno); + } else { + *result = new PosixSequentialFile(fname, f); + return Status::OK(); + } + } + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) { + *result = NULL; + Status s; + int fd = open(fname.c_str(), O_RDONLY); + if (fd < 0) { + s = IOError(fname, errno); + } else if (mmap_limit_.Acquire()) { + uint64_t size; + s = GetFileSize(fname, &size); + if (s.ok()) { + void* base = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (base != MAP_FAILED) { + *result = new PosixMmapReadableFile(fname, base, size, &mmap_limit_); + } else { + s = IOError(fname, errno); + } + } + close(fd); + if (!s.ok()) { + mmap_limit_.Release(); + } + } else { + *result = new PosixRandomAccessFile(fname, fd, &fd_limit_); + } + return s; + } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + Status s; + FILE* f = fopen(fname.c_str(), "w"); + if (f == NULL) { + *result = NULL; + s = IOError(fname, errno); + } else { + *result = new PosixWritableFile(fname, f); + } + return s; + } + + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result) { + Status s; + FILE* f = fopen(fname.c_str(), "a"); + if (f == NULL) { + *result = NULL; + s = IOError(fname, errno); + } else { + *result = new PosixWritableFile(fname, f); + } + return s; + } + + virtual bool FileExists(const std::string& fname) { + return access(fname.c_str(), F_OK) == 0; + } + + virtual Status GetChildren(const std::string& dir, + std::vector* result) { + result->clear(); + DIR* d = opendir(dir.c_str()); + if (d == NULL) { + return IOError(dir, errno); + } + struct dirent* entry; + while ((entry = readdir(d)) != NULL) { + result->push_back(entry->d_name); + } + closedir(d); + return Status::OK(); + } + + virtual Status DeleteFile(const std::string& fname) { + Status result; + if (unlink(fname.c_str()) != 0) { + result = IOError(fname, errno); + } + return result; + } + + virtual Status CreateDir(const std::string& name) { + Status result; + if (mkdir(name.c_str(), 0755) != 0) { + result = IOError(name, errno); + } + return result; + } + + virtual Status DeleteDir(const std::string& name) { + Status result; + if (rmdir(name.c_str()) != 0) { + result = IOError(name, errno); + } + return result; + } + + virtual Status GetFileSize(const std::string& fname, uint64_t* size) { + Status s; + struct stat sbuf; + if (stat(fname.c_str(), &sbuf) != 0) { + *size = 0; + s = IOError(fname, errno); + } else { + *size = sbuf.st_size; + } + return s; + } + + virtual Status RenameFile(const std::string& src, const std::string& target) { + Status result; + if (rename(src.c_str(), target.c_str()) != 0) { + result = IOError(src, errno); + } + return result; + } + + virtual Status LockFile(const std::string& fname, FileLock** lock) { + *lock = NULL; + Status result; + int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644); + if (fd < 0) { + result = IOError(fname, errno); + } else if (!locks_.Insert(fname)) { + close(fd); + result = Status::IOError("lock " + fname, "already held by process"); + } else if (LockOrUnlock(fd, true) == -1) { + result = IOError("lock " + fname, errno); + close(fd); + locks_.Remove(fname); + } else { + PosixFileLock* my_lock = new PosixFileLock; + my_lock->fd_ = fd; + my_lock->name_ = fname; + *lock = my_lock; + } + return result; + } + + virtual Status UnlockFile(FileLock* lock) { + PosixFileLock* my_lock = reinterpret_cast(lock); + Status result; + if (LockOrUnlock(my_lock->fd_, false) == -1) { + result = IOError("unlock", errno); + } + locks_.Remove(my_lock->name_); + close(my_lock->fd_); + delete my_lock; + return result; + } + + virtual void Schedule(void (*function)(void*), void* arg); + + virtual void StartThread(void (*function)(void* arg), void* arg); + + virtual Status GetTestDirectory(std::string* result) { + const char* env = getenv("TEST_TMPDIR"); + if (env && env[0] != '\0') { + *result = env; + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid())); + *result = buf; + } + // Directory may already exist + CreateDir(*result); + return Status::OK(); + } + + static uint64_t gettid() { + pthread_t tid = pthread_self(); + uint64_t thread_id = 0; + memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid))); + return thread_id; + } + + virtual Status NewLogger(const std::string& fname, Logger** result) { + FILE* f = fopen(fname.c_str(), "w"); + if (f == NULL) { + *result = NULL; + return IOError(fname, errno); + } else { + *result = new PosixLogger(f, &PosixEnv::gettid); + return Status::OK(); + } + } + + virtual uint64_t NowMicros() { + struct timeval tv; + gettimeofday(&tv, NULL); + return static_cast(tv.tv_sec) * 1000000 + tv.tv_usec; + } + + virtual void SleepForMicroseconds(int micros) { + usleep(micros); + } + + private: + void PthreadCall(const char* label, int result) { + if (result != 0) { + fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); + abort(); + } + } + + // BGThread() is the body of the background thread + void BGThread(); + static void* BGThreadWrapper(void* arg) { + reinterpret_cast(arg)->BGThread(); + return NULL; + } + + pthread_mutex_t mu_; + pthread_cond_t bgsignal_; + pthread_t bgthread_; + bool started_bgthread_; + + // Entry per Schedule() call + struct BGItem { void* arg; void (*function)(void*); }; + typedef std::deque BGQueue; + BGQueue queue_; + + PosixLockTable locks_; + Limiter mmap_limit_; + Limiter fd_limit_; +}; + +// Return the maximum number of concurrent mmaps. +static int MaxMmaps() { + if (mmap_limit >= 0) { + return mmap_limit; + } + // Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes. + mmap_limit = sizeof(void*) >= 8 ? 1000 : 0; + return mmap_limit; +} + +// Return the maximum number of read-only files to keep open. +static intptr_t MaxOpenFiles() { + if (open_read_only_file_limit >= 0) { + return open_read_only_file_limit; + } + struct rlimit rlim; + if (getrlimit(RLIMIT_NOFILE, &rlim)) { + // getrlimit failed, fallback to hard-coded default. + open_read_only_file_limit = 50; + } else if (rlim.rlim_cur == RLIM_INFINITY) { + open_read_only_file_limit = std::numeric_limits::max(); + } else { + // Allow use of 20% of available file descriptors for read-only files. + open_read_only_file_limit = rlim.rlim_cur / 5; + } + return open_read_only_file_limit; +} + +PosixEnv::PosixEnv() + : started_bgthread_(false), + mmap_limit_(MaxMmaps()), + fd_limit_(MaxOpenFiles()) { + PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL)); + PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL)); +} + +void PosixEnv::Schedule(void (*function)(void*), void* arg) { + PthreadCall("lock", pthread_mutex_lock(&mu_)); + + // Start background thread if necessary + if (!started_bgthread_) { + started_bgthread_ = true; + PthreadCall( + "create thread", + pthread_create(&bgthread_, NULL, &PosixEnv::BGThreadWrapper, this)); + } + + // If the queue is currently empty, the background thread may currently be + // waiting. + if (queue_.empty()) { + PthreadCall("signal", pthread_cond_signal(&bgsignal_)); + } + + // Add to priority queue + queue_.push_back(BGItem()); + queue_.back().function = function; + queue_.back().arg = arg; + + PthreadCall("unlock", pthread_mutex_unlock(&mu_)); +} + +void PosixEnv::BGThread() { + while (true) { + // Wait until there is an item that is ready to run + PthreadCall("lock", pthread_mutex_lock(&mu_)); + while (queue_.empty()) { + PthreadCall("wait", pthread_cond_wait(&bgsignal_, &mu_)); + } + + void (*function)(void*) = queue_.front().function; + void* arg = queue_.front().arg; + queue_.pop_front(); + + PthreadCall("unlock", pthread_mutex_unlock(&mu_)); + (*function)(arg); + } +} + +namespace { +struct StartThreadState { + void (*user_function)(void*); + void* arg; +}; +} +static void* StartThreadWrapper(void* arg) { + StartThreadState* state = reinterpret_cast(arg); + state->user_function(state->arg); + delete state; + return NULL; +} + +void PosixEnv::StartThread(void (*function)(void* arg), void* arg) { + pthread_t t; + StartThreadState* state = new StartThreadState; + state->user_function = function; + state->arg = arg; + PthreadCall("start thread", + pthread_create(&t, NULL, &StartThreadWrapper, state)); +} + +} // namespace + +static pthread_once_t once = PTHREAD_ONCE_INIT; +static Env* default_env; +static void InitDefaultEnv() { default_env = new PosixEnv; } + +void EnvPosixTestHelper::SetReadOnlyFDLimit(int limit) { + assert(default_env == NULL); + open_read_only_file_limit = limit; +} + +void EnvPosixTestHelper::SetReadOnlyMMapLimit(int limit) { + assert(default_env == NULL); + mmap_limit = limit; +} + +Env* Env::Default() { + pthread_once(&once, InitDefaultEnv); + return default_env; +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_posix_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_posix_test.cc new file mode 100644 index 0000000..295f8ae --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_posix_test.cc @@ -0,0 +1,66 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +#include "port/port.h" +#include "util/testharness.h" +#include "util/env_posix_test_helper.h" + +namespace leveldb { + +static const int kDelayMicros = 100000; +static const int kReadOnlyFileLimit = 4; +static const int kMMapLimit = 4; + +class EnvPosixTest { + public: + Env* env_; + EnvPosixTest() : env_(Env::Default()) { } + + static void SetFileLimits(int read_only_file_limit, int mmap_limit) { + EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit); + EnvPosixTestHelper::SetReadOnlyMMapLimit(mmap_limit); + } +}; + +TEST(EnvPosixTest, TestOpenOnRead) { + // Write some test data to a single file that will be opened |n| times. + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string test_file = test_dir + "/open_on_read.txt"; + + FILE* f = fopen(test_file.c_str(), "w"); + ASSERT_TRUE(f != NULL); + const char kFileData[] = "abcdefghijklmnopqrstuvwxyz"; + fputs(kFileData, f); + fclose(f); + + // Open test file some number above the sum of the two limits to force + // open-on-read behavior of POSIX Env leveldb::RandomAccessFile. + const int kNumFiles = kReadOnlyFileLimit + kMMapLimit + 5; + leveldb::RandomAccessFile* files[kNumFiles] = {0}; + for (int i = 0; i < kNumFiles; i++) { + ASSERT_OK(env_->NewRandomAccessFile(test_file, &files[i])); + } + char scratch; + Slice read_result; + for (int i = 0; i < kNumFiles; i++) { + ASSERT_OK(files[i]->Read(i, 1, &read_result, &scratch)); + ASSERT_EQ(kFileData[i], read_result[0]); + } + for (int i = 0; i < kNumFiles; i++) { + delete files[i]; + } + ASSERT_OK(env_->DeleteFile(test_file)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + // All tests currently run with the same read-only file limits. + leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit, + leveldb::kMMapLimit); + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_posix_test_helper.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_posix_test_helper.h new file mode 100644 index 0000000..0386960 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_posix_test_helper.h @@ -0,0 +1,28 @@ +// Copyright 2017 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_ +#define STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_ + +namespace leveldb { + +class EnvPosixTest; + +// A helper for the POSIX Env to facilitate testing. +class EnvPosixTestHelper { + private: + friend class EnvPosixTest; + + // Set the maximum number of read-only files that will be opened. + // Must be called before creating an Env. + static void SetReadOnlyFDLimit(int limit); + + // Set the maximum number of read-only files that will be mapped via mmap. + // Must be called before creating an Env. + static void SetReadOnlyMMapLimit(int limit); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_test.cc new file mode 100644 index 0000000..839ae56 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_test.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +#include "port/port.h" +#include "util/testharness.h" + +namespace leveldb { + +static const int kDelayMicros = 100000; +static const int kReadOnlyFileLimit = 4; +static const int kMMapLimit = 4; + +class EnvTest { + private: + port::Mutex mu_; + std::string events_; + + public: + Env* env_; + EnvTest() : env_(Env::Default()) { } +}; + +static void SetBool(void* ptr) { + reinterpret_cast(ptr)->NoBarrier_Store(ptr); +} + +TEST(EnvTest, RunImmediately) { + port::AtomicPointer called (NULL); + env_->Schedule(&SetBool, &called); + env_->SleepForMicroseconds(kDelayMicros); + ASSERT_TRUE(called.NoBarrier_Load() != NULL); +} + +TEST(EnvTest, RunMany) { + port::AtomicPointer last_id (NULL); + + struct CB { + port::AtomicPointer* last_id_ptr; // Pointer to shared slot + uintptr_t id; // Order# for the execution of this callback + + CB(port::AtomicPointer* p, int i) : last_id_ptr(p), id(i) { } + + static void Run(void* v) { + CB* cb = reinterpret_cast(v); + void* cur = cb->last_id_ptr->NoBarrier_Load(); + ASSERT_EQ(cb->id-1, reinterpret_cast(cur)); + cb->last_id_ptr->Release_Store(reinterpret_cast(cb->id)); + } + }; + + // Schedule in different order than start time + CB cb1(&last_id, 1); + CB cb2(&last_id, 2); + CB cb3(&last_id, 3); + CB cb4(&last_id, 4); + env_->Schedule(&CB::Run, &cb1); + env_->Schedule(&CB::Run, &cb2); + env_->Schedule(&CB::Run, &cb3); + env_->Schedule(&CB::Run, &cb4); + + env_->SleepForMicroseconds(kDelayMicros); + void* cur = last_id.Acquire_Load(); + ASSERT_EQ(4, reinterpret_cast(cur)); +} + +struct State { + port::Mutex mu; + int val; + int num_running; +}; + +static void ThreadBody(void* arg) { + State* s = reinterpret_cast(arg); + s->mu.Lock(); + s->val += 1; + s->num_running -= 1; + s->mu.Unlock(); +} + +TEST(EnvTest, StartThread) { + State state; + state.val = 0; + state.num_running = 3; + for (int i = 0; i < 3; i++) { + env_->StartThread(&ThreadBody, &state); + } + while (true) { + state.mu.Lock(); + int num = state.num_running; + state.mu.Unlock(); + if (num == 0) { + break; + } + env_->SleepForMicroseconds(kDelayMicros); + } + ASSERT_EQ(state.val, 3); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/filter_policy.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/filter_policy.cc new file mode 100644 index 0000000..7b045c8 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/filter_policy.cc @@ -0,0 +1,11 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +namespace leveldb { + +FilterPolicy::~FilterPolicy() { } + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/hash.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/hash.cc new file mode 100644 index 0000000..ed439ce --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/hash.cc @@ -0,0 +1,52 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "util/coding.h" +#include "util/hash.h" + +// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through +// between switch labels. The real definition should be provided externally. +// This one is a fallback version for unsupported compilers. +#ifndef FALLTHROUGH_INTENDED +#define FALLTHROUGH_INTENDED do { } while (0) +#endif + +namespace leveldb { + +uint32_t Hash(const char* data, size_t n, uint32_t seed) { + // Similar to murmur hash + const uint32_t m = 0xc6a4a793; + const uint32_t r = 24; + const char* limit = data + n; + uint32_t h = seed ^ (n * m); + + // Pick up four bytes at a time + while (data + 4 <= limit) { + uint32_t w = DecodeFixed32(data); + data += 4; + h += w; + h *= m; + h ^= (h >> 16); + } + + // Pick up remaining bytes + switch (limit - data) { + case 3: + h += static_cast(data[2]) << 16; + FALLTHROUGH_INTENDED; + case 2: + h += static_cast(data[1]) << 8; + FALLTHROUGH_INTENDED; + case 1: + h += static_cast(data[0]); + h *= m; + h ^= (h >> r); + break; + } + return h; +} + + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/hash.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/hash.h new file mode 100644 index 0000000..8889d56 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/hash.h @@ -0,0 +1,19 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Simple hash function used for internal data structures + +#ifndef STORAGE_LEVELDB_UTIL_HASH_H_ +#define STORAGE_LEVELDB_UTIL_HASH_H_ + +#include +#include + +namespace leveldb { + +extern uint32_t Hash(const char* data, size_t n, uint32_t seed); + +} + +#endif // STORAGE_LEVELDB_UTIL_HASH_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/hash_test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/hash_test.cc new file mode 100644 index 0000000..eaa1c92 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/hash_test.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/hash.h" +#include "util/testharness.h" + +namespace leveldb { + +class HASH { }; + +TEST(HASH, SignedUnsignedIssue) { + const unsigned char data1[1] = {0x62}; + const unsigned char data2[2] = {0xc3, 0x97}; + const unsigned char data3[3] = {0xe2, 0x99, 0xa5}; + const unsigned char data4[4] = {0xe1, 0x80, 0xb9, 0x32}; + const unsigned char data5[48] = { + 0x01, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x18, + 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + ASSERT_EQ(Hash(0, 0, 0xbc9f1d34), 0xbc9f1d34); + ASSERT_EQ( + Hash(reinterpret_cast(data1), sizeof(data1), 0xbc9f1d34), + 0xef1345c4); + ASSERT_EQ( + Hash(reinterpret_cast(data2), sizeof(data2), 0xbc9f1d34), + 0x5b663814); + ASSERT_EQ( + Hash(reinterpret_cast(data3), sizeof(data3), 0xbc9f1d34), + 0x323c078f); + ASSERT_EQ( + Hash(reinterpret_cast(data4), sizeof(data4), 0xbc9f1d34), + 0xed21633a); + ASSERT_EQ( + Hash(reinterpret_cast(data5), sizeof(data5), 0x12345678), + 0xf333dabb); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/histogram.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/histogram.cc new file mode 100644 index 0000000..bb95f58 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/histogram.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include "port/port.h" +#include "util/histogram.h" + +namespace leveldb { + +const double Histogram::kBucketLimit[kNumBuckets] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, + 50, 60, 70, 80, 90, 100, 120, 140, 160, 180, 200, 250, 300, 350, 400, 450, + 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000, 2500, 3000, + 3500, 4000, 4500, 5000, 6000, 7000, 8000, 9000, 10000, 12000, 14000, + 16000, 18000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 60000, + 70000, 80000, 90000, 100000, 120000, 140000, 160000, 180000, 200000, + 250000, 300000, 350000, 400000, 450000, 500000, 600000, 700000, 800000, + 900000, 1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2500000, + 3000000, 3500000, 4000000, 4500000, 5000000, 6000000, 7000000, 8000000, + 9000000, 10000000, 12000000, 14000000, 16000000, 18000000, 20000000, + 25000000, 30000000, 35000000, 40000000, 45000000, 50000000, 60000000, + 70000000, 80000000, 90000000, 100000000, 120000000, 140000000, 160000000, + 180000000, 200000000, 250000000, 300000000, 350000000, 400000000, + 450000000, 500000000, 600000000, 700000000, 800000000, 900000000, + 1000000000, 1200000000, 1400000000, 1600000000, 1800000000, 2000000000, + 2500000000.0, 3000000000.0, 3500000000.0, 4000000000.0, 4500000000.0, + 5000000000.0, 6000000000.0, 7000000000.0, 8000000000.0, 9000000000.0, + 1e200, +}; + +void Histogram::Clear() { + min_ = kBucketLimit[kNumBuckets-1]; + max_ = 0; + num_ = 0; + sum_ = 0; + sum_squares_ = 0; + for (int i = 0; i < kNumBuckets; i++) { + buckets_[i] = 0; + } +} + +void Histogram::Add(double value) { + // Linear search is fast enough for our usage in db_bench + int b = 0; + while (b < kNumBuckets - 1 && kBucketLimit[b] <= value) { + b++; + } + buckets_[b] += 1.0; + if (min_ > value) min_ = value; + if (max_ < value) max_ = value; + num_++; + sum_ += value; + sum_squares_ += (value * value); +} + +void Histogram::Merge(const Histogram& other) { + if (other.min_ < min_) min_ = other.min_; + if (other.max_ > max_) max_ = other.max_; + num_ += other.num_; + sum_ += other.sum_; + sum_squares_ += other.sum_squares_; + for (int b = 0; b < kNumBuckets; b++) { + buckets_[b] += other.buckets_[b]; + } +} + +double Histogram::Median() const { + return Percentile(50.0); +} + +double Histogram::Percentile(double p) const { + double threshold = num_ * (p / 100.0); + double sum = 0; + for (int b = 0; b < kNumBuckets; b++) { + sum += buckets_[b]; + if (sum >= threshold) { + // Scale linearly within this bucket + double left_point = (b == 0) ? 0 : kBucketLimit[b-1]; + double right_point = kBucketLimit[b]; + double left_sum = sum - buckets_[b]; + double right_sum = sum; + double pos = (threshold - left_sum) / (right_sum - left_sum); + double r = left_point + (right_point - left_point) * pos; + if (r < min_) r = min_; + if (r > max_) r = max_; + return r; + } + } + return max_; +} + +double Histogram::Average() const { + if (num_ == 0.0) return 0; + return sum_ / num_; +} + +double Histogram::StandardDeviation() const { + if (num_ == 0.0) return 0; + double variance = (sum_squares_ * num_ - sum_ * sum_) / (num_ * num_); + return sqrt(variance); +} + +std::string Histogram::ToString() const { + std::string r; + char buf[200]; + snprintf(buf, sizeof(buf), + "Count: %.0f Average: %.4f StdDev: %.2f\n", + num_, Average(), StandardDeviation()); + r.append(buf); + snprintf(buf, sizeof(buf), + "Min: %.4f Median: %.4f Max: %.4f\n", + (num_ == 0.0 ? 0.0 : min_), Median(), max_); + r.append(buf); + r.append("------------------------------------------------------\n"); + const double mult = 100.0 / num_; + double sum = 0; + for (int b = 0; b < kNumBuckets; b++) { + if (buckets_[b] <= 0.0) continue; + sum += buckets_[b]; + snprintf(buf, sizeof(buf), + "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ", + ((b == 0) ? 0.0 : kBucketLimit[b-1]), // left + kBucketLimit[b], // right + buckets_[b], // count + mult * buckets_[b], // percentage + mult * sum); // cumulative percentage + r.append(buf); + + // Add hash marks based on percentage; 20 marks for 100%. + int marks = static_cast(20*(buckets_[b] / num_) + 0.5); + r.append(marks, '#'); + r.push_back('\n'); + } + return r; +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/histogram.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/histogram.h new file mode 100644 index 0000000..1ef9f3c --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/histogram.h @@ -0,0 +1,42 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ +#define STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ + +#include + +namespace leveldb { + +class Histogram { + public: + Histogram() { } + ~Histogram() { } + + void Clear(); + void Add(double value); + void Merge(const Histogram& other); + + std::string ToString() const; + + private: + double min_; + double max_; + double num_; + double sum_; + double sum_squares_; + + enum { kNumBuckets = 154 }; + static const double kBucketLimit[kNumBuckets]; + double buckets_[kNumBuckets]; + + double Median() const; + double Percentile(double p) const; + double Average() const; + double StandardDeviation() const; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/logging.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/logging.cc new file mode 100644 index 0000000..ca6b324 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/logging.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/logging.h" + +#include +#include +#include +#include +#include "leveldb/env.h" +#include "leveldb/slice.h" + +namespace leveldb { + +void AppendNumberTo(std::string* str, uint64_t num) { + char buf[30]; + snprintf(buf, sizeof(buf), "%llu", (unsigned long long) num); + str->append(buf); +} + +void AppendEscapedStringTo(std::string* str, const Slice& value) { + for (size_t i = 0; i < value.size(); i++) { + char c = value[i]; + if (c >= ' ' && c <= '~') { + str->push_back(c); + } else { + char buf[10]; + snprintf(buf, sizeof(buf), "\\x%02x", + static_cast(c) & 0xff); + str->append(buf); + } + } +} + +std::string NumberToString(uint64_t num) { + std::string r; + AppendNumberTo(&r, num); + return r; +} + +std::string EscapeString(const Slice& value) { + std::string r; + AppendEscapedStringTo(&r, value); + return r; +} + +bool ConsumeDecimalNumber(Slice* in, uint64_t* val) { + uint64_t v = 0; + int digits = 0; + while (!in->empty()) { + char c = (*in)[0]; + if (c >= '0' && c <= '9') { + ++digits; + const int delta = (c - '0'); + static const uint64_t kMaxUint64 = ~static_cast(0); + if (v > kMaxUint64/10 || + (v == kMaxUint64/10 && delta > kMaxUint64%10)) { + // Overflow + return false; + } + v = (v * 10) + delta; + in->remove_prefix(1); + } else { + break; + } + } + *val = v; + return (digits > 0); +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/logging.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/logging.h new file mode 100644 index 0000000..1b450d2 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/logging.h @@ -0,0 +1,43 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Must not be included from any .h files to avoid polluting the namespace +// with macros. + +#ifndef STORAGE_LEVELDB_UTIL_LOGGING_H_ +#define STORAGE_LEVELDB_UTIL_LOGGING_H_ + +#include +#include +#include +#include "port/port.h" + +namespace leveldb { + +class Slice; +class WritableFile; + +// Append a human-readable printout of "num" to *str +extern void AppendNumberTo(std::string* str, uint64_t num); + +// Append a human-readable printout of "value" to *str. +// Escapes any non-printable characters found in "value". +extern void AppendEscapedStringTo(std::string* str, const Slice& value); + +// Return a human-readable printout of "num" +extern std::string NumberToString(uint64_t num); + +// Return a human-readable version of "value". +// Escapes any non-printable characters found in "value". +extern std::string EscapeString(const Slice& value); + +// Parse a human-readable number from "*in" into *value. On success, +// advances "*in" past the consumed number and sets "*val" to the +// numeric value. Otherwise, returns false and leaves *in in an +// unspecified state. +extern bool ConsumeDecimalNumber(Slice* in, uint64_t* val); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_LOGGING_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/mutexlock.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/mutexlock.h new file mode 100644 index 0000000..1ff5a9e --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/mutexlock.h @@ -0,0 +1,41 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ +#define STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ + +#include "port/port.h" +#include "port/thread_annotations.h" + +namespace leveldb { + +// Helper class that locks a mutex on construction and unlocks the mutex when +// the destructor of the MutexLock object is invoked. +// +// Typical usage: +// +// void MyClass::MyMethod() { +// MutexLock l(&mu_); // mu_ is an instance variable +// ... some complex code, possibly with multiple return paths ... +// } + +class SCOPED_LOCKABLE MutexLock { + public: + explicit MutexLock(port::Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu) + : mu_(mu) { + this->mu_->Lock(); + } + ~MutexLock() UNLOCK_FUNCTION() { this->mu_->Unlock(); } + + private: + port::Mutex *const mu_; + // No copying allowed + MutexLock(const MutexLock&); + void operator=(const MutexLock&); +}; + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/options.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/options.cc new file mode 100644 index 0000000..b5e6227 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/options.cc @@ -0,0 +1,30 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/options.h" + +#include "leveldb/comparator.h" +#include "leveldb/env.h" + +namespace leveldb { + +Options::Options() + : comparator(BytewiseComparator()), + create_if_missing(false), + error_if_exists(false), + paranoid_checks(false), + env(Env::Default()), + info_log(NULL), + write_buffer_size(4<<20), + max_open_files(1000), + block_cache(NULL), + block_size(4096), + block_restart_interval(16), + max_file_size(2<<20), + compression(kSnappyCompression), + reuse_logs(false), + filter_policy(NULL) { +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/posix_logger.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/posix_logger.h new file mode 100644 index 0000000..9741b1a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/posix_logger.h @@ -0,0 +1,98 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Logger implementation that can be shared by all environments +// where enough posix functionality is available. + +#ifndef STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ +#define STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ + +#include +#include +#include +#include +#include "leveldb/env.h" + +namespace leveldb { + +class PosixLogger : public Logger { + private: + FILE* file_; + uint64_t (*gettid_)(); // Return the thread id for the current thread + public: + PosixLogger(FILE* f, uint64_t (*gettid)()) : file_(f), gettid_(gettid) { } + virtual ~PosixLogger() { + fclose(file_); + } + virtual void Logv(const char* format, va_list ap) { + const uint64_t thread_id = (*gettid_)(); + + // We try twice: the first time with a fixed-size stack allocated buffer, + // and the second time with a much larger dynamically allocated buffer. + char buffer[500]; + for (int iter = 0; iter < 2; iter++) { + char* base; + int bufsize; + if (iter == 0) { + bufsize = sizeof(buffer); + base = buffer; + } else { + bufsize = 30000; + base = new char[bufsize]; + } + char* p = base; + char* limit = base + bufsize; + + struct timeval now_tv; + gettimeofday(&now_tv, NULL); + const time_t seconds = now_tv.tv_sec; + struct tm t; + localtime_r(&seconds, &t); + p += snprintf(p, limit - p, + "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", + t.tm_year + 1900, + t.tm_mon + 1, + t.tm_mday, + t.tm_hour, + t.tm_min, + t.tm_sec, + static_cast(now_tv.tv_usec), + static_cast(thread_id)); + + // Print the message + if (p < limit) { + va_list backup_ap; + va_copy(backup_ap, ap); + p += vsnprintf(p, limit - p, format, backup_ap); + va_end(backup_ap); + } + + // Truncate to available space if necessary + if (p >= limit) { + if (iter == 0) { + continue; // Try again with larger buffer + } else { + p = limit - 1; + } + } + + // Add newline if necessary + if (p == base || p[-1] != '\n') { + *p++ = '\n'; + } + + assert(p <= limit); + fwrite(base, 1, p - base, file_); + fflush(file_); + if (base != buffer) { + delete[] base; + } + break; + } + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/random.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/random.h new file mode 100644 index 0000000..ddd51b1 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/random.h @@ -0,0 +1,64 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_RANDOM_H_ +#define STORAGE_LEVELDB_UTIL_RANDOM_H_ + +#include + +namespace leveldb { + +// A very simple random number generator. Not especially good at +// generating truly random bits, but good enough for our needs in this +// package. +class Random { + private: + uint32_t seed_; + public: + explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { + // Avoid bad seeds. + if (seed_ == 0 || seed_ == 2147483647L) { + seed_ = 1; + } + } + uint32_t Next() { + static const uint32_t M = 2147483647L; // 2^31-1 + static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 + // We are computing + // seed_ = (seed_ * A) % M, where M = 2^31-1 + // + // seed_ must not be zero or M, or else all subsequent computed values + // will be zero or M respectively. For all other values, seed_ will end + // up cycling through every number in [1,M-1] + uint64_t product = seed_ * A; + + // Compute (product % M) using the fact that ((x << 31) % M) == x. + seed_ = static_cast((product >> 31) + (product & M)); + // The first reduction may overflow by 1 bit, so we may need to + // repeat. mod == M is not possible; using > allows the faster + // sign-bit-based test. + if (seed_ > M) { + seed_ -= M; + } + return seed_; + } + // Returns a uniformly distributed value in the range [0..n-1] + // REQUIRES: n > 0 + uint32_t Uniform(int n) { return Next() % n; } + + // Randomly returns true ~"1/n" of the time, and false otherwise. + // REQUIRES: n > 0 + bool OneIn(int n) { return (Next() % n) == 0; } + + // Skewed: pick "base" uniformly from range [0,max_log] and then + // return "base" random bits. The effect is to pick a number in the + // range [0,2^max_log-1] with exponential bias towards smaller numbers. + uint32_t Skewed(int max_log) { + return Uniform(1 << Uniform(max_log + 1)); + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_RANDOM_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/status.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/status.cc new file mode 100644 index 0000000..a44f35b --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/status.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "port/port.h" +#include "leveldb/status.h" + +namespace leveldb { + +const char* Status::CopyState(const char* state) { + uint32_t size; + memcpy(&size, state, sizeof(size)); + char* result = new char[size + 5]; + memcpy(result, state, size + 5); + return result; +} + +Status::Status(Code code, const Slice& msg, const Slice& msg2) { + assert(code != kOk); + const uint32_t len1 = msg.size(); + const uint32_t len2 = msg2.size(); + const uint32_t size = len1 + (len2 ? (2 + len2) : 0); + char* result = new char[size + 5]; + memcpy(result, &size, sizeof(size)); + result[4] = static_cast(code); + memcpy(result + 5, msg.data(), len1); + if (len2) { + result[5 + len1] = ':'; + result[6 + len1] = ' '; + memcpy(result + 7 + len1, msg2.data(), len2); + } + state_ = result; +} + +std::string Status::ToString() const { + if (state_ == NULL) { + return "OK"; + } else { + char tmp[30]; + const char* type; + switch (code()) { + case kOk: + type = "OK"; + break; + case kNotFound: + type = "NotFound: "; + break; + case kCorruption: + type = "Corruption: "; + break; + case kNotSupported: + type = "Not implemented: "; + break; + case kInvalidArgument: + type = "Invalid argument: "; + break; + case kIOError: + type = "IO error: "; + break; + default: + snprintf(tmp, sizeof(tmp), "Unknown code(%d): ", + static_cast(code())); + type = tmp; + break; + } + std::string result(type); + uint32_t length; + memcpy(&length, state_, sizeof(length)); + result.append(state_ + 5, length); + return result; + } +} + +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testharness.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testharness.cc new file mode 100644 index 0000000..402fab3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testharness.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/testharness.h" + +#include +#include +#include +#include + +namespace leveldb { +namespace test { + +namespace { +struct Test { + const char* base; + const char* name; + void (*func)(); +}; +std::vector* tests; +} + +bool RegisterTest(const char* base, const char* name, void (*func)()) { + if (tests == NULL) { + tests = new std::vector; + } + Test t; + t.base = base; + t.name = name; + t.func = func; + tests->push_back(t); + return true; +} + +int RunAllTests() { + const char* matcher = getenv("LEVELDB_TESTS"); + + int num = 0; + if (tests != NULL) { + for (size_t i = 0; i < tests->size(); i++) { + const Test& t = (*tests)[i]; + if (matcher != NULL) { + std::string name = t.base; + name.push_back('.'); + name.append(t.name); + if (strstr(name.c_str(), matcher) == NULL) { + continue; + } + } + fprintf(stderr, "==== Test %s.%s\n", t.base, t.name); + (*t.func)(); + ++num; + } + } + fprintf(stderr, "==== PASSED %d tests\n", num); + return 0; +} + +std::string TmpDir() { + std::string dir; + Status s = Env::Default()->GetTestDirectory(&dir); + ASSERT_TRUE(s.ok()) << s.ToString(); + return dir; +} + +int RandomSeed() { + const char* env = getenv("TEST_RANDOM_SEED"); + int result = (env != NULL ? atoi(env) : 301); + if (result <= 0) { + result = 301; + } + return result; +} + +} // namespace test +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testharness.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testharness.h new file mode 100644 index 0000000..da4fe68 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testharness.h @@ -0,0 +1,138 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ +#define STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ + +#include +#include +#include +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "util/random.h" + +namespace leveldb { +namespace test { + +// Run some of the tests registered by the TEST() macro. If the +// environment variable "LEVELDB_TESTS" is not set, runs all tests. +// Otherwise, runs only the tests whose name contains the value of +// "LEVELDB_TESTS" as a substring. E.g., suppose the tests are: +// TEST(Foo, Hello) { ... } +// TEST(Foo, World) { ... } +// LEVELDB_TESTS=Hello will run the first test +// LEVELDB_TESTS=o will run both tests +// LEVELDB_TESTS=Junk will run no tests +// +// Returns 0 if all tests pass. +// Dies or returns a non-zero value if some test fails. +extern int RunAllTests(); + +// Return the directory to use for temporary storage. +extern std::string TmpDir(); + +// Return a randomization seed for this run. Typically returns the +// same number on repeated invocations of this binary, but automated +// runs may be able to vary the seed. +extern int RandomSeed(); + +// An instance of Tester is allocated to hold temporary state during +// the execution of an assertion. +class Tester { + private: + bool ok_; + const char* fname_; + int line_; + std::stringstream ss_; + + public: + Tester(const char* f, int l) + : ok_(true), fname_(f), line_(l) { + } + + ~Tester() { + if (!ok_) { + fprintf(stderr, "%s:%d:%s\n", fname_, line_, ss_.str().c_str()); + exit(1); + } + } + + Tester& Is(bool b, const char* msg) { + if (!b) { + ss_ << " Assertion failure " << msg; + ok_ = false; + } + return *this; + } + + Tester& IsOk(const Status& s) { + if (!s.ok()) { + ss_ << " " << s.ToString(); + ok_ = false; + } + return *this; + } + +#define BINARY_OP(name,op) \ + template \ + Tester& name(const X& x, const Y& y) { \ + if (! (x op y)) { \ + ss_ << " failed: " << x << (" " #op " ") << y; \ + ok_ = false; \ + } \ + return *this; \ + } + + BINARY_OP(IsEq, ==) + BINARY_OP(IsNe, !=) + BINARY_OP(IsGe, >=) + BINARY_OP(IsGt, >) + BINARY_OP(IsLe, <=) + BINARY_OP(IsLt, <) +#undef BINARY_OP + + // Attach the specified value to the error message if an error has occurred + template + Tester& operator<<(const V& value) { + if (!ok_) { + ss_ << " " << value; + } + return *this; + } +}; + +#define ASSERT_TRUE(c) ::leveldb::test::Tester(__FILE__, __LINE__).Is((c), #c) +#define ASSERT_OK(s) ::leveldb::test::Tester(__FILE__, __LINE__).IsOk((s)) +#define ASSERT_EQ(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsEq((a),(b)) +#define ASSERT_NE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsNe((a),(b)) +#define ASSERT_GE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGe((a),(b)) +#define ASSERT_GT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGt((a),(b)) +#define ASSERT_LE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLe((a),(b)) +#define ASSERT_LT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLt((a),(b)) + +#define TCONCAT(a,b) TCONCAT1(a,b) +#define TCONCAT1(a,b) a##b + +#define TEST(base,name) \ +class TCONCAT(_Test_,name) : public base { \ + public: \ + void _Run(); \ + static void _RunIt() { \ + TCONCAT(_Test_,name) t; \ + t._Run(); \ + } \ +}; \ +bool TCONCAT(_Test_ignored_,name) = \ + ::leveldb::test::RegisterTest(#base, #name, &TCONCAT(_Test_,name)::_RunIt); \ +void TCONCAT(_Test_,name)::_Run() + +// Register the specified test. Typically not used directly, but +// invoked via the macro expansion of TEST. +extern bool RegisterTest(const char* base, const char* name, void (*func)()); + + +} // namespace test +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testutil.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testutil.cc new file mode 100644 index 0000000..bee56bf --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testutil.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/testutil.h" + +#include "util/random.h" + +namespace leveldb { +namespace test { + +Slice RandomString(Random* rnd, int len, std::string* dst) { + dst->resize(len); + for (int i = 0; i < len; i++) { + (*dst)[i] = static_cast(' ' + rnd->Uniform(95)); // ' ' .. '~' + } + return Slice(*dst); +} + +std::string RandomKey(Random* rnd, int len) { + // Make sure to generate a wide variety of characters so we + // test the boundary conditions for short-key optimizations. + static const char kTestChars[] = { + '\0', '\1', 'a', 'b', 'c', 'd', 'e', '\xfd', '\xfe', '\xff' + }; + std::string result; + for (int i = 0; i < len; i++) { + result += kTestChars[rnd->Uniform(sizeof(kTestChars))]; + } + return result; +} + + +extern Slice CompressibleString(Random* rnd, double compressed_fraction, + size_t len, std::string* dst) { + int raw = static_cast(len * compressed_fraction); + if (raw < 1) raw = 1; + std::string raw_data; + RandomString(rnd, raw, &raw_data); + + // Duplicate the random data until we have filled "len" bytes + dst->clear(); + while (dst->size() < len) { + dst->append(raw_data); + } + dst->resize(len); + return Slice(*dst); +} + +} // namespace test +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testutil.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testutil.h new file mode 100644 index 0000000..d7e4583 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testutil.h @@ -0,0 +1,63 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_TESTUTIL_H_ +#define STORAGE_LEVELDB_UTIL_TESTUTIL_H_ + +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "util/random.h" + +namespace leveldb { +namespace test { + +// Store in *dst a random string of length "len" and return a Slice that +// references the generated data. +extern Slice RandomString(Random* rnd, int len, std::string* dst); + +// Return a random key with the specified length that may contain interesting +// characters (e.g. \x00, \xff, etc.). +extern std::string RandomKey(Random* rnd, int len); + +// Store in *dst a string of length "len" that will compress to +// "N*compressed_fraction" bytes and return a Slice that references +// the generated data. +extern Slice CompressibleString(Random* rnd, double compressed_fraction, + size_t len, std::string* dst); + +// A wrapper that allows injection of errors. +class ErrorEnv : public EnvWrapper { + public: + bool writable_file_error_; + int num_writable_file_errors_; + + ErrorEnv() : EnvWrapper(Env::Default()), + writable_file_error_(false), + num_writable_file_errors_(0) { } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + if (writable_file_error_) { + ++num_writable_file_errors_; + *result = NULL; + return Status::IOError(fname, "fake error"); + } + return target()->NewWritableFile(fname, result); + } + + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result) { + if (writable_file_error_) { + ++num_writable_file_errors_; + *result = NULL; + return Status::IOError(fname, "fake error"); + } + return target()->NewAppendableFile(fname, result); + } +}; + +} // namespace test +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_TESTUTIL_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb.gyp b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb.gyp new file mode 100644 index 0000000..8a8f840 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/leveldb.gyp @@ -0,0 +1,281 @@ +{ + "variables": { + 'openssl_fips': '' + }, + "targets": [{ + "target_name": "leveldb", + "variables": { + "ldbversion": "1.20" + }, + "type": "static_library", + "standalone_static_library": 1, + "dependencies": [ + "../snappy/snappy.gyp:snappy" + ], + "direct_dependent_settings": { + "include_dirs": [ + "leveldb-<(ldbversion)/include/", + "leveldb-<(ldbversion)/port/", + "leveldb-<(ldbversion)/util", + "leveldb-<(ldbversion)/" + ] + }, + "defines": [ + "SNAPPY=1" + ], + "include_dirs": [ + "leveldb-<(ldbversion)/", + "leveldb-<(ldbversion)/include/" + ], + "conditions": [ + ["OS == 'win'", { + "conditions": [ + ["MSVS_VERSION != '2015' and MSVS_VERSION != '2013'", { + "include_dirs": [ "leveldb-<(ldbversion)/port/win" ] + }] + ], + "include_dirs": [ + "port-libuv/" + ], + "defines": [ + "LEVELDB_PLATFORM_UV=1", + "NOMINMAX=1", + "_HAS_EXCEPTIONS=0" + ], + "sources": [ + "port-libuv/port_uv.cc", + "port-libuv/env_win.cc", + "port-libuv/win_logger.cc" + ], + "msvs_settings": { + "VCCLCompilerTool": { + "RuntimeTypeInfo": "false", + "EnableFunctionLevelLinking": "true", + "ExceptionHandling": "2", + "DisableSpecificWarnings": [ "4355", "4530" ,"4267", "4244" ] + } + } + }, { + "sources": [ + "leveldb-<(ldbversion)/port/port_posix.cc", + "leveldb-<(ldbversion)/port/port_posix.h", + "leveldb-<(ldbversion)/util/env_posix.cc" + ], + "defines": [ + "LEVELDB_PLATFORM_POSIX=1" + ], + "ccflags": [ + "-fno-builtin-memcmp", + "-fPIC" + ], + "cflags": [ "-std=c++0x" ], + "cflags!": [ "-fno-tree-vrp" ] + }], + ["OS != 'win'" and "OS != 'freebsd'", { + "cflags": [ + "-Wno-sign-compare", + "-Wno-unused-but-set-variable" + ] + }], + ["OS == 'linux'", { + "defines": [ + "OS_LINUX=1" + ], + "libraries": [ + "-lpthread" + ], + "ccflags": [ + "-pthread" + ] + }], + ["OS == 'freebsd'", { + "defines": [ + "OS_FREEBSD=1", + "_REENTRANT=1" + ], + "libraries": [ + "-lpthread" + ], + "ccflags": [ + "-pthread" + ], + "cflags": [ + "-Wno-sign-compare" + ] + }], + ["OS == 'openbsd'", { + "defines": [ + "OS_OPENBSD=1", + "_REENTRANT=1" + ], + "libraries": [ + "-lpthread" + ], + "ccflags": [ + "-pthread" + ], + "cflags": [ + "-Wno-sign-compare" + ] + }], + ["OS == 'solaris'", { + "defines": [ + "OS_SOLARIS=1", + "_REENTRANT=1" + ], + "libraries": [ + "-lrt", + "-lpthread" + ], + "ccflags": [ + "-pthread" + ] + }], + ["OS == 'ios'", { + "defines": [ + "OS_IOS=1" + ], + "libraries": [], + "ccflags": [], + "xcode_settings": { + "WARNING_CFLAGS": [ + "-Wno-sign-compare", + "-Wno-unused-variable", + "-Wno-unused-function" + ] + } + }], + ["OS == 'mac'", { + "defines": [ + "OS_MACOSX=1" + ], + "libraries": [], + "ccflags": [], + "xcode_settings": { + "WARNING_CFLAGS": [ + "-Wno-sign-compare", + "-Wno-unused-variable", + "-Wno-unused-function" + ], + + # Set minimum target version because we're building on newer + "MACOSX_DEPLOYMENT_TARGET": "10.7", + + # Build universal binary to support M1 (Apple silicon) + "OTHER_CFLAGS": [ + "-arch x86_64", + "-arch arm64" + ] + } + }], + ["OS == 'android'", { + "defines": [ + "OS_ANDROID=1", + "_REENTRANT=1" + ], + "libraries": [ + "-lpthread" + ], + "ccflags": [ + "-pthread", + "-fno-builtin-memcmp" + ], + "cflags": [ + "-fPIC" + ], + "cflags!": [ + "-fPIE", + "-mfloat-abi=hard", + "-Wno-unused-but-set-variable" + ] + }], + ["target_arch == 'arm'", { + "cflags": [ + "-mfloat-abi=hard" + ] + }] + ], + "sources": [ + "leveldb-<(ldbversion)/db/builder.cc", + "leveldb-<(ldbversion)/db/builder.h", + "leveldb-<(ldbversion)/db/db_impl.cc", + "leveldb-<(ldbversion)/db/db_impl.h", + "leveldb-<(ldbversion)/db/db_iter.cc", + "leveldb-<(ldbversion)/db/db_iter.h", + "leveldb-<(ldbversion)/db/filename.cc", + "leveldb-<(ldbversion)/db/filename.h", + "leveldb-<(ldbversion)/db/dbformat.cc", + "leveldb-<(ldbversion)/db/dbformat.h", + "leveldb-<(ldbversion)/db/log_format.h", + "leveldb-<(ldbversion)/db/log_reader.cc", + "leveldb-<(ldbversion)/db/log_reader.h", + "leveldb-<(ldbversion)/db/log_writer.cc", + "leveldb-<(ldbversion)/db/log_writer.h", + "leveldb-<(ldbversion)/db/memtable.cc", + "leveldb-<(ldbversion)/db/memtable.h", + "leveldb-<(ldbversion)/db/repair.cc", + "leveldb-<(ldbversion)/db/skiplist.h", + "leveldb-<(ldbversion)/db/snapshot.h", + "leveldb-<(ldbversion)/db/table_cache.cc", + "leveldb-<(ldbversion)/db/table_cache.h", + "leveldb-<(ldbversion)/db/version_edit.cc", + "leveldb-<(ldbversion)/db/version_edit.h", + "leveldb-<(ldbversion)/db/version_set.cc", + "leveldb-<(ldbversion)/db/version_set.h", + "leveldb-<(ldbversion)/db/write_batch.cc", + "leveldb-<(ldbversion)/db/write_batch_internal.h", + "leveldb-<(ldbversion)/helpers/memenv/memenv.cc", + "leveldb-<(ldbversion)/helpers/memenv/memenv.h", + "leveldb-<(ldbversion)/include/leveldb/cache.h", + "leveldb-<(ldbversion)/include/leveldb/comparator.h", + "leveldb-<(ldbversion)/include/leveldb/db.h", + "leveldb-<(ldbversion)/include/leveldb/dumpfile.h", + "leveldb-<(ldbversion)/include/leveldb/env.h", + "leveldb-<(ldbversion)/include/leveldb/filter_policy.h", + "leveldb-<(ldbversion)/include/leveldb/iterator.h", + "leveldb-<(ldbversion)/include/leveldb/options.h", + "leveldb-<(ldbversion)/include/leveldb/slice.h", + "leveldb-<(ldbversion)/include/leveldb/status.h", + "leveldb-<(ldbversion)/include/leveldb/table.h", + "leveldb-<(ldbversion)/include/leveldb/table_builder.h", + "leveldb-<(ldbversion)/include/leveldb/write_batch.h", + "leveldb-<(ldbversion)/port/port.h", + "leveldb-<(ldbversion)/port/port_posix_sse.cc", + "leveldb-<(ldbversion)/table/block.cc", + "leveldb-<(ldbversion)/table/block.h", + "leveldb-<(ldbversion)/table/block_builder.cc", + "leveldb-<(ldbversion)/table/block_builder.h", + "leveldb-<(ldbversion)/table/filter_block.cc", + "leveldb-<(ldbversion)/table/filter_block.h", + "leveldb-<(ldbversion)/table/format.cc", + "leveldb-<(ldbversion)/table/format.h", + "leveldb-<(ldbversion)/table/iterator.cc", + "leveldb-<(ldbversion)/table/iterator_wrapper.h", + "leveldb-<(ldbversion)/table/merger.cc", + "leveldb-<(ldbversion)/table/merger.h", + "leveldb-<(ldbversion)/table/table.cc", + "leveldb-<(ldbversion)/table/table_builder.cc", + "leveldb-<(ldbversion)/table/two_level_iterator.cc", + "leveldb-<(ldbversion)/table/two_level_iterator.h", + "leveldb-<(ldbversion)/util/arena.cc", + "leveldb-<(ldbversion)/util/arena.h", + "leveldb-<(ldbversion)/util/bloom.cc", + "leveldb-<(ldbversion)/util/cache.cc", + "leveldb-<(ldbversion)/util/coding.cc", + "leveldb-<(ldbversion)/util/coding.h", + "leveldb-<(ldbversion)/util/comparator.cc", + "leveldb-<(ldbversion)/util/crc32c.cc", + "leveldb-<(ldbversion)/util/crc32c.h", + "leveldb-<(ldbversion)/util/env.cc", + "leveldb-<(ldbversion)/util/filter_policy.cc", + "leveldb-<(ldbversion)/util/hash.cc", + "leveldb-<(ldbversion)/util/hash.h", + "leveldb-<(ldbversion)/util/logging.cc", + "leveldb-<(ldbversion)/util/logging.h", + "leveldb-<(ldbversion)/util/mutexlock.h", + "leveldb-<(ldbversion)/util/options.cc", + "leveldb-<(ldbversion)/util/random.h", + "leveldb-<(ldbversion)/util/status.cc" + ] + }] +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/atomic_pointer_win.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/atomic_pointer_win.h new file mode 100644 index 0000000..12ad2ea --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/atomic_pointer_win.h @@ -0,0 +1,48 @@ +#include +#include + +// credit: https://groups.google.com/forum/#!msg/leveldb/VuECZMnsob4/F6pGPGaK-XwJ + +namespace leveldb { +namespace port { + +class AtomicPointer { + private: + void* rep_; + + public: + AtomicPointer () {} + explicit AtomicPointer(void* v) { + InterlockedExchangePointer(&rep_, v); + } + + // Read and return the stored pointer with the guarantee that no + // later memory access (read or write) by this thread can be + // reordered ahead of this read. + inline void* Acquire_Load() const { + void* r; + InterlockedExchangePointer(&r, rep_ ); + return r; + } + + // Set v as the stored pointer with the guarantee that no earlier + // memory access (read or write) by this thread can be reordered + // after this store. + inline void Release_Store(void* v) { + InterlockedExchangePointer(&rep_, v); + } + + // Read the stored pointer with no ordering guarantees. + inline void* NoBarrier_Load() const { + void* r = reinterpret_cast(rep_); + return r; + } + + // Set va as the stored pointer with no ordering guarantees. + inline void NoBarrier_Store(void* v) { + rep_ = reinterpret_cast(v); + } +}; + +} // namespace port +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/env_win.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/env_win.cc new file mode 100644 index 0000000..c1f5aaa --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/env_win.cc @@ -0,0 +1,692 @@ +/* + * originally from http://code.google.com/r/kkowalczyk-leveldb/ + * code by Krzysztof Kowalczyk kkowalczyk kowalczyk@gmail.com + * See also http://blog.kowalczyk.info/software/leveldb-for-windows/index.html + */ + + +#include +#include +#include +#include + +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "port/port.h" +#include "util/logging.h" +#include "win_logger.h" + +#if defined(_MSC_VER) +# ifdef DeleteFile +# undef DeleteFile +# endif +#endif + +// To properly support file names on Windows we should be using Unicode +// (WCHAR) strings. To accomodate existing interface which uses std::string, +// we use the following convention: +// * all filenames that we return (e.g. from GetTestDirectory()) are +// utf8-encoded +// * we'll try to interpret all input file names as if they're +// utf8-encoded. If they're not valid utf8 strings, we'll try +// to interpret them according to a current code page +// This just works for names that don't use characters outside ascii +// and for those that do, the caller needs to be aware of this convention +// whenever it bubbles up to the user-level API. + +namespace leveldb { + +static Status IOError(const std::string& context, DWORD err = (DWORD)-1) { + char *err_msg = NULL; + Status s; + if ((DWORD)-1 == err) + err = GetLastError(); + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&err_msg, 0, NULL); + if (!err_msg) + return Status::IOError(context); + s = Status::IOError(context, err_msg); + LocalFree(err_msg); + return s; +} + +class WinSequentialFile: public SequentialFile { +private: + std::string fname_; + HANDLE file_; + +public: + WinSequentialFile(const std::string& fname, HANDLE f) + : fname_(fname), file_(f) { } + virtual ~WinSequentialFile() { CloseHandle(file_); } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + DWORD n2 = n; + DWORD r = 0; + BOOL ok = ReadFile(file_, (void*)scratch, n2, &r, NULL); + *result = Slice(scratch, r); + if (!ok) { + // We leave status as ok if we hit the end of the file + if (GetLastError() != ERROR_HANDLE_EOF) { + return IOError(fname_); + } + } + return Status::OK(); + } + + virtual Status Skip(uint64_t n) { + LARGE_INTEGER pos; + pos.QuadPart = n; + DWORD res = SetFilePointerEx(file_, pos, NULL, FILE_CURRENT); + if (res == 0) + return IOError(fname_); + return Status::OK(); + } +}; + +class WinRandomAccessFile: public RandomAccessFile { + private: + std::string fname_; + HANDLE file_; + + public: + WinRandomAccessFile(const std::string& fname, HANDLE file) + : fname_(fname), file_(file) { } + virtual ~WinRandomAccessFile() { CloseHandle(file_); } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + OVERLAPPED overlapped = { 0 }; + overlapped.Offset = static_cast(offset); + overlapped.OffsetHigh = static_cast(offset >> 32); + DWORD bytes_read = 0; + BOOL success = ReadFile(file_, scratch, n, &bytes_read, &overlapped); + *result = Slice(scratch, bytes_read); + return success != FALSE ? Status::OK() : Status::IOError(fname_); + } +}; + +class WinWritableFile : public WritableFile { +private: + std::string name_; + HANDLE file_; + +public: + WinWritableFile(std::string name, HANDLE h) : name_(name), file_(h) { + } + + virtual ~WinWritableFile() { + Close(); + } + + virtual Status Append(const Slice& data) { + DWORD n = data.size(); + DWORD pos = 0; + while (pos < n) { + DWORD written = 0; + BOOL ok = WriteFile(file_, data.data() + pos, n - pos, &written, NULL); + if (!ok) + return IOError(name_+ "Append: cannot write"); + pos += written; + } + return Status::OK(); + } + + virtual Status Close() { + if (INVALID_HANDLE_VALUE == file_) + return Status::OK(); + Status s = Sync(); + CloseHandle(file_); + file_ = INVALID_HANDLE_VALUE; + return s; + } + + virtual Status Flush() { + return Status::OK(); + } + + virtual Status Sync() { + BOOL ok = FlushFileBuffers(file_); + if (!ok) + return IOError(name_); + return Status::OK(); + } +}; + +namespace { + +#define DIR_SEP_CHAR L'\\' +#define DIR_SEP_STR L"\\" + +WCHAR *ToWcharFromCodePage(const char *src, UINT cp) { + int required_buf_size = MultiByteToWideChar(cp, 0, src, -1, NULL, 0); + if (0 == required_buf_size) // indicates an error + return NULL; + WCHAR *res = reinterpret_cast(malloc(sizeof(WCHAR) * required_buf_size)); + if (!res) + return NULL; + MultiByteToWideChar(cp, 0, src, -1, res, required_buf_size); + return res; +} + +// try to convert to WCHAR string trying most common code pages +// to be as permissive as we can be +WCHAR *ToWcharPermissive(const char *s) { + WCHAR *ws = ToWcharFromCodePage(s, CP_UTF8); + if (ws != NULL) + return ws; + ws = ToWcharFromCodePage(s, CP_ACP); + if (ws != NULL) + return ws; + ws = ToWcharFromCodePage(s, CP_OEMCP); + return ws; +} + +char *ToUtf8(const WCHAR *s) { + int required_buf_size = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL); + char *res = (char*)malloc(sizeof(char) * required_buf_size); + if (!res) + return NULL; + WideCharToMultiByte(CP_UTF8, 0, s, -1, res, required_buf_size, NULL, NULL); + return res; +} + +static size_t WstrLen(const WCHAR *s) { + if (NULL == s) + return 0; + return wcslen(s); +} + +static WCHAR *WstrJoin(const WCHAR *s1, const WCHAR *s2, const WCHAR *s3=NULL) { + size_t s1_len = WstrLen(s1); + size_t s2_len = WstrLen(s2); + size_t s3_len = WstrLen(s3); + size_t len =s1_len + s2_len + s3_len + 1; + WCHAR *res = (WCHAR*)malloc(sizeof(WCHAR) * len); + if (!res) + return NULL; + WCHAR *tmp = res; + if (s1 != NULL) { + memcpy(tmp, s1, s1_len * sizeof(WCHAR)); + tmp += s1_len; + } + if (s2 != NULL) { + memcpy(tmp, s2, s2_len * sizeof(WCHAR)); + tmp += s2_len; + } + if (s3 != NULL) { + memcpy(tmp, s3, s3_len * sizeof(WCHAR)); + tmp += s3_len; + } + *tmp = 0; + return res; +} + +static bool WstrEndsWith(const WCHAR *s1, WCHAR c) { + size_t len = WstrLen(s1); + return ((len > 0) && (s1[len-1] == c)); +} + +static WCHAR *WstrPathJoin(const WCHAR *s1, const WCHAR *s2) { + if (WstrEndsWith(s1, DIR_SEP_CHAR)) + return WstrJoin(s1, s2); + return WstrJoin(s1, DIR_SEP_STR, s2); +} + +// Return true if s is "." or "..", which are 2 directories +// we should skip when enumerating a directory +static bool SkipDir(const WCHAR *s) { + if (*s == L'.') { + if (s[1] == 0) + return true; + return ((s[1] == '.') && (s[2] == 0)); + } + return false; +} + +class WinFileLock : public FileLock { + public: + WinFileLock(const std::string &fname, HANDLE file) + : fname_(fname), file_(file) { + } + + virtual ~WinFileLock() { + Close(); + } + + bool Close() { + bool ok = true; + if (file_ != INVALID_HANDLE_VALUE) + ok = (CloseHandle(file_) != FALSE); + file_ = INVALID_HANDLE_VALUE; + return ok; + } + + std::string fname_; + HANDLE file_; +}; + +class WinEnv : public Env { + public: + WinEnv(); + virtual ~WinEnv() { + fprintf(stderr, "Destroying Env::Default()\n"); + //exit(1); + } + + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) { + *result = NULL; + WCHAR *file_name = ToWcharPermissive(fname.c_str()); + if (file_name == NULL) { + return Status::InvalidArgument("Invalid file name"); + } + HANDLE h = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + free((void*)file_name); + if (h == INVALID_HANDLE_VALUE) { + return IOError(fname); + } + *result = new WinSequentialFile(fname, h); + return Status::OK(); + } + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) { + *result = NULL; + WCHAR *file_name = ToWcharPermissive(fname.c_str()); + if (file_name == NULL) { + return Status::InvalidArgument("Invalid file name"); + } + HANDLE h = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + free((void*)file_name); + if (h == INVALID_HANDLE_VALUE) { + return IOError(fname); + } + *result = new WinRandomAccessFile(fname, h); + return Status::OK(); + } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + *result = NULL; + WCHAR *file_name = ToWcharPermissive(fname.c_str()); + if (file_name == NULL) + return Status::InvalidArgument("Invalid file name"); + HANDLE h = CreateFileW(file_name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + free((void*)file_name); + if (h == INVALID_HANDLE_VALUE) { + return IOError(fname); + } + *result = new WinWritableFile(fname, h); + return Status::OK(); + } + + virtual bool FileExists(const std::string& fname) { + WCHAR *file_name = ToWcharPermissive(fname.c_str()); + if (file_name == NULL) + return false; + + WIN32_FILE_ATTRIBUTE_DATA file_info; + BOOL res = GetFileAttributesExW(file_name, GetFileExInfoStandard, &file_info); + free((void*)file_name); + if (0 == res) + return false; + + if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) + return false; + return true; + } + + virtual Status GetChildren(const std::string& dir, + std::vector* result) { + result->clear(); + WCHAR *dir_name = ToWcharPermissive(dir.c_str()); + if (dir_name == NULL) + return Status::InvalidArgument("Invalid file name"); + WCHAR *pattern = WstrPathJoin(dir_name, L"*"); + free(dir_name); + if (NULL == pattern) + return Status::InvalidArgument("Invalid file name"); + WIN32_FIND_DATAW file_data; + HANDLE h = FindFirstFileW(pattern, &file_data); + free(pattern); + if (INVALID_HANDLE_VALUE == h) { + if (ERROR_FILE_NOT_FOUND == GetLastError()) + return Status::OK(); + return IOError(dir); + } + for (;;) { + WCHAR *s = file_data.cFileName; + if (!SkipDir(s)) { + char *s2 = ToUtf8(s); + result->push_back(s2); + free(s2); + } + if (FALSE == FindNextFileW(h, &file_data)) + break; + } + FindClose(h); + return Status::OK(); + } + + virtual Status DeleteFile(const std::string& fname) { + WCHAR *file_path = ToWcharPermissive(fname.c_str()); + if (file_path == NULL) + return Status::InvalidArgument("Invalid file name"); + + BOOL ok = DeleteFileW(file_path); + free(file_path); + if (!ok) { + DWORD err = GetLastError(); + if ((ERROR_PATH_NOT_FOUND == err) || (ERROR_FILE_NOT_FOUND == err)) + return Status::OK(); + return IOError("DeleteFile " + fname); + } + return Status::OK(); + } + + bool CreateDirIfNotExists(const WCHAR *dir) { + BOOL ok = CreateDirectoryW(dir, NULL); + if (ok) + return true; + return (ERROR_ALREADY_EXISTS == GetLastError()); + } + + bool DirExists(const WCHAR *dir) { + WIN32_FILE_ATTRIBUTE_DATA file_info; + BOOL res = GetFileAttributesExW(dir, GetFileExInfoStandard, &file_info); + if (0 == res) + return false; + + return (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + } + + WCHAR *WstrDupN(const WCHAR *s, size_t len) { + void *res = malloc((len + 1) * sizeof(WCHAR)); + if (!res) + return NULL; + memcpy(res, s, len * sizeof(WCHAR)); + WCHAR *res2 = reinterpret_cast(res); + res2[len] = 0; + return res2; + } + + bool IsPathSep(WCHAR c) { + return (c == '\\') || (c == '/'); + } + + WCHAR *GetPathParent(const WCHAR *path) { + const WCHAR *last_sep = NULL; + const WCHAR *tmp = path; + // find the last path separator + // (ignoring one at the end of the string) + while (*tmp) { + if (IsPathSep(*tmp)) { + if (0 != tmp[1]) + last_sep = tmp; + } + ++tmp; + } + if (NULL == last_sep) + return NULL; + size_t len = last_sep - path; + return WstrDupN(path, len); + } + + bool CreateDirRecursive(WCHAR *dir) { + WCHAR *parent = GetPathParent(dir); + bool ok = true; + if (parent && !DirExists(parent)) { + ok = CreateDirRecursive(parent); + } + free(parent); + if (!ok) + return false; + return CreateDirIfNotExists(dir); + } + + virtual Status CreateDir(const std::string& name) { + WCHAR *dir = ToWcharPermissive(name.c_str()); + if (dir == NULL) + return Status::InvalidArgument("Invalid file name"); + bool ok = CreateDirRecursive(dir); + free(dir); + if (!ok) + return IOError(name); + return Status::OK(); + } + +#if 1 + virtual Status DeleteDir(const std::string& name) { + WCHAR *dir = ToWcharPermissive(name.c_str()); + if (dir == NULL) + return Status::InvalidArgument("Invalid file name"); + BOOL ok = RemoveDirectoryW(dir); + free(dir); + if (!ok) + return IOError(name); + return Status::OK(); + } +#else + virtual Status DeleteDir(const std::string& dirname) { + WCHAR *dir = ToWcharPermissive(dirname.c_str()); + if (dir == NULL) + return Status::InvalidArgument("Invalid file name"); + + SHFILEOPSTRUCTW fileop = { 0 }; + fileop.wFunc = FO_DELETE; + fileop.pFrom = (const WCHAR*)dir; + fileop.fFlags = FOF_NO_UI; + int res = SHFileOperationW(&fileop); + free((void*)dir); + if (res == 0 && fileop.fAnyOperationsAborted == FALSE) + return Status::OK(); + return IOError("DeleteDir " + dirname); + } +#endif + + virtual Status GetFileSize(const std::string& fname, uint64_t* size) { + + WCHAR *file_name = ToWcharPermissive(fname.c_str()); + if (file_name == NULL) + return Status::InvalidArgument("Invalid file name"); + HANDLE h = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + free(file_name); + if (h == INVALID_HANDLE_VALUE) + return IOError("GetFileSize " + fname); + + // Not using GetFileAttributesEx() as it doesn't interact well with symlinks, etc. + LARGE_INTEGER lsize; + BOOL ok = GetFileSizeEx(h, &lsize); + CloseHandle(h); + if (!ok) + return IOError("GetFileSize " + fname); + + *size = static_cast(lsize.QuadPart); + return Status::OK(); + } + + virtual Status RenameFile(const std::string& src, const std::string& target) { + WCHAR *src2 = ToWcharPermissive(src.c_str()); + WCHAR *target2 = ToWcharPermissive(target.c_str()); + if ((src2 == NULL) || (target2 == NULL)) { + free(src2); + free(target2); + return Status::InvalidArgument("Invalid file name"); + } + BOOL ok = MoveFileExW(src2, target2, MOVEFILE_REPLACE_EXISTING); + free(src2); + free(target2); + if (!ok) + return IOError("RenameFile " + src + " " + target); + return Status::OK(); + } + + virtual Status LockFile(const std::string& fname, FileLock** lock) { + *lock = NULL; + WCHAR *file_name = ToWcharPermissive(fname.c_str()); + if (file_name == NULL) { + return Status::InvalidArgument("Invalid file name"); + } + HANDLE h = CreateFileW(file_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + free((void*)file_name); + if (h == INVALID_HANDLE_VALUE) { + return IOError("LockFile " + fname); + } + *lock = new WinFileLock(fname, h); + return Status::OK(); + } + + virtual Status UnlockFile(FileLock* lock) { + Status s; + WinFileLock* my_lock = reinterpret_cast(lock); + if (!my_lock->Close()) { + s = Status::IOError(my_lock->fname_, "Could not close lock file."); + } + delete my_lock; + return Status::OK(); + } + + virtual void Schedule(void (*function)(void*), void* arg); + + virtual void StartThread(void (*function)(void* arg), void* arg); + + virtual Status GetTestDirectory(std::string* result) { + WCHAR buf[MAX_PATH]; + DWORD res = GetTempPathW(MAX_PATH, buf); + if (0 == res) { + return IOError("Can't get test directory"); + } + char *s = ToUtf8(buf); + if (!s) { + return IOError("Can't get test directory"); + } + *result = std::string(s); + free(s); + return Status::OK(); + } + + virtual Status NewLogger(const std::string& fname, Logger** result) { + *result = NULL; + FILE* f = fopen(fname.c_str(), "wt"); + if (f == NULL) + return Status::IOError(fname, strerror(errno)); + *result = new WinLogger(f); + return Status::OK(); + } + + virtual uint64_t NowMicros() { + LARGE_INTEGER count; + QueryPerformanceCounter(&count); + return count.QuadPart * 1000000i64 / freq_.QuadPart; + } + + virtual void SleepForMicroseconds(int micros) { + // round up to the next millisecond + Sleep((micros + 999) / 1000); + } + +private: + LARGE_INTEGER freq_; + + // BGThread() is the body of the background thread + void BGThread(); + + static unsigned __stdcall BGThreadWrapper(void* arg) { + (reinterpret_cast(arg))->BGThread(); + _endthreadex(0); + return 0; + } + + leveldb::port::Mutex mu_; + leveldb::port::CondVar bgsignal_; + HANDLE bgthread_; + + // Entry per Schedule() call + struct BGItem { void* arg; void (*function)(void*); }; + typedef std::deque BGQueue; + BGQueue queue_; +}; + + +WinEnv::WinEnv() : bgthread_(NULL), bgsignal_(&mu_) { + QueryPerformanceFrequency(&freq_); +} + +void WinEnv::Schedule(void (*function)(void*), void* arg) { + mu_.Lock(); + + // Start background thread if necessary + if (NULL == bgthread_) { + bgthread_ = (HANDLE)_beginthreadex(NULL, 0, &WinEnv::BGThreadWrapper, this, 0, NULL); + } + + // Add to priority queue + queue_.push_back(BGItem()); + queue_.back().function = function; + queue_.back().arg = arg; + + mu_.Unlock(); + + bgsignal_.Signal(); +} + +void WinEnv::BGThread() { + while (true) { + // Wait until there is an item that is ready to run + mu_.Lock(); + + while (queue_.empty()) { + bgsignal_.Wait(); + } + + void (*function)(void*) = queue_.front().function; + void* arg = queue_.front().arg; + queue_.pop_front(); + + mu_.Unlock(); + (*function)(arg); + } + // TODO: CloseHandle(bgthread_) ?? +} + +namespace { +struct StartThreadState { + void (*user_function)(void*); + void* arg; + HANDLE threadHandle; +}; +} + +static unsigned __stdcall StartThreadWrapper(void* arg) { + StartThreadState* state = reinterpret_cast(arg); + state->user_function(state->arg); + _endthreadex(0); + CloseHandle(state->threadHandle); + delete state; + return 0; +} + +void WinEnv::StartThread(void (*function)(void* arg), void* arg) { + StartThreadState* state = new StartThreadState; + state->user_function = function; + state->arg = arg; + state->threadHandle = (HANDLE)_beginthreadex(NULL, 0, &StartThreadWrapper, state, 0, NULL); +} +} + +static Env* default_env; +static void InitDefaultEnv() { default_env = new WinEnv(); } +static leveldb::port::Mutex default_env_mutex; + +Env* Env::Default() { + default_env_mutex.Lock(); + if (NULL == default_env) + InitDefaultEnv(); + default_env_mutex.Unlock(); + return default_env; +} + +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/port_uv.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/port_uv.cc new file mode 100644 index 0000000..860fd4d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/port_uv.cc @@ -0,0 +1,36 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "port_uv.h" + +#include +#include +#include +#include "util/logging.h" + +namespace leveldb { +namespace port { + +Mutex::Mutex() { uv_mutex_init(&mu_); } + +Mutex::~Mutex() { uv_mutex_destroy(&mu_); } + +void Mutex::Lock() { uv_mutex_lock(&mu_); } + +void Mutex::Unlock() { uv_mutex_unlock(&mu_); } + +CondVar::CondVar(Mutex* mu) : mu_(mu) { uv_cond_init(&cv_); } + +CondVar::~CondVar() { uv_cond_destroy(&cv_); } + +void CondVar::Wait() { uv_cond_wait(&cv_, &mu_->mu_); } + +void CondVar::Signal() { uv_cond_signal(&cv_); } + +void CondVar::SignalAll() { uv_cond_broadcast(&cv_); } + +void InitOnce(OnceType* once, void (*initializer)()) { uv_once(once, initializer); } + +} // namespace port +} // namespace leveldb diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/port_uv.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/port_uv.h new file mode 100644 index 0000000..15477ca --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/port_uv.h @@ -0,0 +1,157 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +#ifndef STORAGE_LEVELDB_PORT_PORT_LIBUV_H_ +#define STORAGE_LEVELDB_PORT_PORT_LIBUV_H_ + +#undef PLATFORM_IS_LITTLE_ENDIAN +#if defined(OS_MACOSX) + #include + #if defined(__DARWIN_LITTLE_ENDIAN) && defined(__DARWIN_BYTE_ORDER) + #define PLATFORM_IS_LITTLE_ENDIAN \ + (__DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN) + #endif +#elif defined(OS_SOLARIS) + #include + #ifdef _LITTLE_ENDIAN + #define PLATFORM_IS_LITTLE_ENDIAN true + #else + #define PLATFORM_IS_LITTLE_ENDIAN false + #endif +#elif defined(OS_FREEBSD) || defined(OS_OPENBSD) || defined(OS_NETBSD) ||\ + defined(OS_DRAGONFLYBSD) || defined(OS_ANDROID) + #include + #include +#elif defined(_MSC_VER) +#if _MSC_VER < 1600 + #include "stdint-msvc2008.h" +#endif + #define PLATFORM_IS_LITTLE_ENDIAN true +#if _MSC_VER < 1900 + #define snprintf _snprintf +#endif + #define close _close + #define fread_unlocked _fread_nolock +#else + #include +#endif + +#include + +#ifdef SNAPPY +#include +#endif +#include +#include +#if defined(_MSC_VER) + #include "atomic_pointer_win.h" + #undef small +#else + #include "port/atomic_pointer.h" +#endif + +#ifndef PLATFORM_IS_LITTLE_ENDIAN +#define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) +#endif + +#if defined(OS_MACOSX) || defined(OS_SOLARIS) || defined(OS_FREEBSD) ||\ + defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) ||\ + defined(OS_ANDROID) +// Use fread/fwrite/fflush on platforms without _unlocked variants +#define fread_unlocked fread +#define fwrite_unlocked fwrite +#define fflush_unlocked fflush +#endif + +#if defined(OS_MACOSX) || defined(OS_FREEBSD) ||\ + defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) +// Use fsync() on platforms without fdatasync() +#define fdatasync fsync +#endif + +namespace leveldb { +namespace port { + +static const bool kLittleEndian = PLATFORM_IS_LITTLE_ENDIAN; +#undef PLATFORM_IS_LITTLE_ENDIAN + +class CondVar; + +class Mutex { + public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); + void AssertHeld() { } + + private: + friend class CondVar; + uv_mutex_t mu_; + + // No copying + Mutex(const Mutex&); + void operator=(const Mutex&); +}; + +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + void Wait(); + void Signal(); + void SignalAll(); + private: + uv_cond_t cv_; + Mutex* mu_; +}; + +typedef uv_once_t OnceType; +#define LEVELDB_ONCE_INIT UV_ONCE_INIT +extern void InitOnce(OnceType* once, void (*initializer)()); + +inline bool Snappy_Compress(const char* input, size_t length, + ::std::string* output) { +#ifdef SNAPPY + output->resize(snappy::MaxCompressedLength(length)); + size_t outlen; + snappy::RawCompress(input, length, &(*output)[0], &outlen); + output->resize(outlen); + return true; +#endif + + return false; +} + +inline bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result) { +#ifdef SNAPPY + return snappy::GetUncompressedLength(input, length, result); +#else + return false; +#endif +} + +inline bool Snappy_Uncompress(const char* input, size_t length, + char* output) { +#ifdef SNAPPY + return snappy::RawUncompress(input, length, output); +#else + return false; +#endif +} + +inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { + return false; +} + +uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size); + +} // namespace port +} // namespace leveldb + +#endif // STORAGE_LEVELDB_PORT_PORT_LIBUV_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/port_uv.h.bak b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/port_uv.h.bak new file mode 100644 index 0000000..304b5a1 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/port_uv.h.bak @@ -0,0 +1,153 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +#ifndef STORAGE_LEVELDB_PORT_PORT_LIBUV_H_ +#define STORAGE_LEVELDB_PORT_PORT_LIBUV_H_ + +#undef PLATFORM_IS_LITTLE_ENDIAN +#if defined(OS_MACOSX) + #include + #if defined(__DARWIN_LITTLE_ENDIAN) && defined(__DARWIN_BYTE_ORDER) + #define PLATFORM_IS_LITTLE_ENDIAN \ + (__DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN) + #endif +#elif defined(OS_SOLARIS) + #include + #ifdef _LITTLE_ENDIAN + #define PLATFORM_IS_LITTLE_ENDIAN true + #else + #define PLATFORM_IS_LITTLE_ENDIAN false + #endif +#elif defined(OS_FREEBSD) || defined(OS_OPENBSD) || defined(OS_NETBSD) ||\ + defined(OS_DRAGONFLYBSD) || defined(OS_ANDROID) + #include + #include +#elif defined(_MSC_VER) + #define PLATFORM_IS_LITTLE_ENDIAN true + #define snprintf _snprintf + #define close _close + #define fread_unlocked _fread_nolock +#else + #include +#endif + +typedef intptr_t ssize_t; +#define _SSIZE_T_ +#define _SSIZE_T_DEFINED + +#include + +#ifdef SNAPPY +#include +#endif +#if defined(_MSC_VER) && _MSC_VER < 1600 +# include "stdint-msvc2008.h" +#else +# include +#endif +#include +#include "port/atomic_pointer.h" + +#ifndef PLATFORM_IS_LITTLE_ENDIAN +#define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) +#endif + +#if defined(OS_MACOSX) || defined(OS_SOLARIS) || defined(OS_FREEBSD) ||\ + defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) ||\ + defined(OS_ANDROID) +// Use fread/fwrite/fflush on platforms without _unlocked variants +#define fread_unlocked fread +#define fwrite_unlocked fwrite +#define fflush_unlocked fflush +#endif + +#if defined(OS_MACOSX) || defined(OS_FREEBSD) ||\ + defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) +// Use fsync() on platforms without fdatasync() +#define fdatasync fsync +#endif + +namespace leveldb { +namespace port { + +static const bool kLittleEndian = PLATFORM_IS_LITTLE_ENDIAN; +#undef PLATFORM_IS_LITTLE_ENDIAN + +class CondVar; + +class Mutex { + public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); + void AssertHeld() { } + + private: + friend class CondVar; + uv_mutex_t mu_; + + // No copying + Mutex(const Mutex&); + void operator=(const Mutex&); +}; + +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + void Wait(); + void Signal(); + void SignalAll(); + private: + uv_cond_t cv_; + Mutex* mu_; +}; + +typedef uv_once_t OnceType; +#define LEVELDB_ONCE_INIT 0 +extern void InitOnce(OnceType* once, void (*initializer)()); + +inline bool Snappy_Compress(const char* input, size_t length, + ::std::string* output) { +#ifdef SNAPPY + output->resize(snappy::MaxCompressedLength(length)); + size_t outlen; + snappy::RawCompress(input, length, &(*output)[0], &outlen); + output->resize(outlen); + return true; +#endif + + return false; +} + +inline bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result) { +#ifdef SNAPPY + return snappy::GetUncompressedLength(input, length, result); +#else + return false; +#endif +} + +inline bool Snappy_Uncompress(const char* input, size_t length, + char* output) { +#ifdef SNAPPY + return snappy::RawUncompress(input, length, output); +#else + return false; +#endif +} + +inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { + return false; +} + +} // namespace port +} // namespace leveldb + +#endif // STORAGE_LEVELDB_PORT_PORT_LIBUV_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/stdint-msvc2008.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/stdint-msvc2008.h new file mode 100644 index 0000000..d02608a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/stdint-msvc2008.h @@ -0,0 +1,247 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2008 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +# include +#ifdef __cplusplus +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + + +#endif // _MSC_STDINT_H_ ] diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/uv_condvar.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/uv_condvar.h new file mode 100644 index 0000000..f3c7e03 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/uv_condvar.h @@ -0,0 +1,45 @@ +// uv_cond_* backport +// Lifted from the Node 0.9 version of libuv for Node 0.8 compatibility +// https://github.com/joyent/libuv/ + +// libuv copyright notice: + +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +* IN THE SOFTWARE. +*/ + +#ifndef LEVELDB_PORT_LIBUV_CONVAR_H_ +#define LEVELDB_PORT_LIBUV_CONVAR_H_ + +#include + +#ifdef LDB_UV_POSIX + typedef pthread_cond_t ldb_uv_cond_t; +#endif + + +UV_EXTERN int ldb_uv_cond_init(ldb_uv_cond_t* cond); +UV_EXTERN void ldb_uv_cond_destroy(ldb_uv_cond_t* cond); +UV_EXTERN void ldb_uv_cond_signal(ldb_uv_cond_t* cond); +UV_EXTERN void ldb_uv_cond_broadcast(ldb_uv_cond_t* cond); +UV_EXTERN void ldb_uv_cond_wait(ldb_uv_cond_t* cond, uv_mutex_t* mutex); +UV_EXTERN int ldb_uv_cond_timedwait(ldb_uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout); + +#endif // LEVELDB_PORT_LIBUV_CONVAR_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/uv_condvar_posix.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/uv_condvar_posix.cc new file mode 100644 index 0000000..d56f558 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/uv_condvar_posix.cc @@ -0,0 +1,139 @@ +// uv_cond_* backport +// Lifted from the Node 0.9 version of libuv for Node 0.8 compatibility +// https://github.com/joyent/libuv/ + +// libuv copyright notice: + +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +* IN THE SOFTWARE. +*/ + +#include +#include "uv_condvar.h" +#include /* abort */ +#include /* ETIMEDOUT */ + +#undef NANOSEC +#define NANOSEC ((uint64_t) 1e9) + +#if defined(__APPLE__) && defined(__MACH__) + +int ldb_uv_cond_init(ldb_uv_cond_t* cond) { + if (pthread_cond_init(cond, NULL)) + return -1; + else + return 0; +} + +#else /* !(defined(__APPLE__) && defined(__MACH__)) */ + +int ldb_uv_cond_init(ldb_uv_cond_t* cond) { + pthread_condattr_t attr; + + if (pthread_condattr_init(&attr)) + return -1; + + if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) + goto error2; + + if (pthread_cond_init(cond, &attr)) + goto error2; + + if (pthread_condattr_destroy(&attr)) + goto error; + + return 0; + +error: + pthread_cond_destroy(cond); +error2: + pthread_condattr_destroy(&attr); + return -1; +} + +#endif /* defined(__APPLE__) && defined(__MACH__) */ + +void ldb_uv_cond_destroy(ldb_uv_cond_t* cond) { + if (pthread_cond_destroy(cond)) + abort(); +} + +void ldb_uv_cond_signal(ldb_uv_cond_t* cond) { + if (pthread_cond_signal(cond)) + abort(); +} + +void ldb_uv_cond_broadcast(ldb_uv_cond_t* cond) { + if (pthread_cond_broadcast(cond)) + abort(); +} + +void ldb_uv_cond_wait(ldb_uv_cond_t* cond, uv_mutex_t* mutex) { + if (pthread_cond_wait(cond, mutex)) + abort(); +} + +#if defined(__APPLE__) && defined(__MACH__) + +int ldb_uv_cond_timedwait(ldb_uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { + int r; + struct timeval tv; + struct timespec ts; + uint64_t abstime; + + gettimeofday(&tv, NULL); + abstime = tv.tv_sec * 1e9 + tv.tv_usec * 1e3 + timeout; + ts.tv_sec = abstime / NANOSEC; + ts.tv_nsec = abstime % NANOSEC; + r = pthread_cond_timedwait(cond, mutex, &ts); + + if (r == 0) + return 0; + + if (r == ETIMEDOUT) + return -1; + + abort(); + return -1; /* Satisfy the compiler. */ +} + +#else /* !(defined(__APPLE__) && defined(__MACH__)) */ + +int ldb_uv_cond_timedwait(ldb_uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { + int r; + struct timespec ts; + uint64_t abstime; + + abstime = uv_hrtime() + timeout; + ts.tv_sec = abstime / NANOSEC; + ts.tv_nsec = abstime % NANOSEC; + r = pthread_cond_timedwait(cond, mutex, &ts); + + if (r == 0) + return 0; + + if (r == ETIMEDOUT) + return -1; + + abort(); + return -1; /* Satisfy the compiler. */ +} + +#endif /* defined(__APPLE__) && defined(__MACH__) */ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/win_logger.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/win_logger.cc new file mode 100644 index 0000000..a2cec75 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/win_logger.cc @@ -0,0 +1,79 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "win_logger.h" + +#include + +namespace leveldb { + +void WinLogger::Logv(const char* format, va_list ap) { + const uint64_t thread_id = static_cast(::GetCurrentThreadId()); + + // We try twice: the first time with a fixed-size stack allocated buffer, + // and the second time with a much larger dynamically allocated buffer. + char buffer[500]; + + for (int iter = 0; iter < 2; iter++) { + char* base; + int bufsize; + if (iter == 0) { + bufsize = sizeof(buffer); + base = buffer; + } else { + bufsize = 30000; + base = new char[bufsize]; + } + + char* p = base; + char* limit = base + bufsize; + + SYSTEMTIME st; + + // GetSystemTime returns UTC time, we want local time! + ::GetLocalTime(&st); + + p += _snprintf_s(p, limit - p, _TRUNCATE, + "%04d/%02d/%02d-%02d:%02d:%02d.%03d %llx ", + st.wYear, + st.wMonth, + st.wDay, + st.wHour, + st.wMinute, + st.wSecond, + st.wMilliseconds, + static_cast(thread_id)); + + // Print the message + if (p < limit) { + va_list backup_ap = ap; + p += vsnprintf(p, limit - p, format, backup_ap); + va_end(backup_ap); + } + + // Truncate to available space if necessary + if (p >= limit) { + if (iter == 0) { + continue; // Try again with larger buffer + } else { + p = limit - 1; + } + } + + // Add newline if necessary + if (p == base || p[-1] != '\n') { + *p++ = '\n'; + } + + assert(p <= limit); + fwrite(base, 1, p - base, file_); + fflush(file_); + if (base != buffer) { + delete[] base; + } + break; + } +} + +} \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/win_logger.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/win_logger.h new file mode 100644 index 0000000..b155d5c --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/leveldb/port-libuv/win_logger.h @@ -0,0 +1,28 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// Logger implementation for Windows + +#ifndef STORAGE_LEVELDB_UTIL_WIN_LOGGER_H_ +#define STORAGE_LEVELDB_UTIL_WIN_LOGGER_H_ + +#include +#include "leveldb/env.h" + +namespace leveldb { + +class WinLogger : public Logger { + private: + FILE* file_; + public: + explicit WinLogger(FILE* f) : file_(f) { assert(file_); } + virtual ~WinLogger() { + fclose(file_); + } + virtual void Logv(const char* format, va_list ap); + +}; + +} +#endif // STORAGE_LEVELDB_UTIL_WIN_LOGGER_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/freebsd/config.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/freebsd/config.h new file mode 100644 index 0000000..5ed0ace --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/freebsd/config.h @@ -0,0 +1,135 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Define to 1 if the compiler supports __builtin_ctz and friends. */ +#define HAVE_BUILTIN_CTZ 1 + +/* Define to 1 if the compiler supports __builtin_expect. */ +#define HAVE_BUILTIN_EXPECT 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_BYTESWAP_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Use the gflags package for command-line parsing. */ +/* #undef HAVE_GFLAGS */ + +/* Defined when Google Test is available. */ +/* #undef HAVE_GTEST */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `fastlz' library (-lfastlz). */ +/* #undef HAVE_LIBFASTLZ */ + +/* Define to 1 if you have the `lzf' library (-llzf). */ +/* #undef HAVE_LIBLZF */ + +/* Define to 1 if you have the `lzo2' library (-llzo2). */ +/* #undef HAVE_LIBLZO2 */ + +/* Define to 1 if you have the `quicklz' library (-lquicklz). */ +/* #undef HAVE_LIBQUICKLZ */ + +/* Define to 1 if you have the `z' library (-lz). */ +#define HAVE_LIBZ 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_BYTESWAP_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_ENDIAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "snappy" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "snappy" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "snappy 1.1.4" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "snappy" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.1.4" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "1.1.4" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Define to `int' if does not define. */ +/* #undef ssize_t */ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/freebsd/snappy-stubs-public.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/freebsd/snappy-stubs-public.h new file mode 100644 index 0000000..3ce2b59 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/freebsd/snappy-stubs-public.h @@ -0,0 +1,100 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: sesse@google.com (Steinar H. Gunderson) +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Various type stubs for the open-source version of Snappy. +// +// This file cannot include config.h, as it is included from snappy.h, +// which is a public header. Instead, snappy-stubs-public.h is generated by +// from snappy-stubs-public.h.in at configure time. + +#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ +#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ + +#if 1 +#include +#endif + +#if 1 +#include +#endif + +#if 1 +#include +#endif + +#define SNAPPY_MAJOR 1 +#define SNAPPY_MINOR 1 +#define SNAPPY_PATCHLEVEL 4 +#define SNAPPY_VERSION \ + ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL) + +#include + +namespace snappy { + +#if 1 +typedef int8_t int8; +typedef uint8_t uint8; +typedef int16_t int16; +typedef uint16_t uint16; +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#else +typedef signed char int8; +typedef unsigned char uint8; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +typedef long long int64; +typedef unsigned long long uint64; +#endif + +typedef std::string string; + +#ifndef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +#if !1 +// Windows does not have an iovec type, yet the concept is universally useful. +// It is simple to define it ourselves, so we put it inside our own namespace. +struct iovec { + void* iov_base; + size_t iov_len; +}; +#endif + +} // namespace snappy + +#endif // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/linux/config.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/linux/config.h new file mode 100644 index 0000000..11f632f --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/linux/config.h @@ -0,0 +1,135 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Define to 1 if the compiler supports __builtin_ctz and friends. */ +#define HAVE_BUILTIN_CTZ 1 + +/* Define to 1 if the compiler supports __builtin_expect. */ +#define HAVE_BUILTIN_EXPECT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_BYTESWAP_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Use the gflags package for command-line parsing. */ +/* #undef HAVE_GFLAGS */ + +/* Defined when Google Test is available. */ +/* #undef HAVE_GTEST */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `fastlz' library (-lfastlz). */ +/* #undef HAVE_LIBFASTLZ */ + +/* Define to 1 if you have the `lzf' library (-llzf). */ +/* #undef HAVE_LIBLZF */ + +/* Define to 1 if you have the `lzo2' library (-llzo2). */ +/* #undef HAVE_LIBLZO2 */ + +/* Define to 1 if you have the `quicklz' library (-lquicklz). */ +/* #undef HAVE_LIBQUICKLZ */ + +/* Define to 1 if you have the `z' library (-lz). */ +#define HAVE_LIBZ 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_BYTESWAP_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_ENDIAN_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "snappy" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "snappy" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "snappy 1.1.4" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "snappy" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.1.4" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "1.1.4" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Define to `int' if does not define. */ +/* #undef ssize_t */ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/linux/snappy-stubs-public.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/linux/snappy-stubs-public.h new file mode 100644 index 0000000..3ce2b59 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/linux/snappy-stubs-public.h @@ -0,0 +1,100 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: sesse@google.com (Steinar H. Gunderson) +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Various type stubs for the open-source version of Snappy. +// +// This file cannot include config.h, as it is included from snappy.h, +// which is a public header. Instead, snappy-stubs-public.h is generated by +// from snappy-stubs-public.h.in at configure time. + +#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ +#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ + +#if 1 +#include +#endif + +#if 1 +#include +#endif + +#if 1 +#include +#endif + +#define SNAPPY_MAJOR 1 +#define SNAPPY_MINOR 1 +#define SNAPPY_PATCHLEVEL 4 +#define SNAPPY_VERSION \ + ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL) + +#include + +namespace snappy { + +#if 1 +typedef int8_t int8; +typedef uint8_t uint8; +typedef int16_t int16; +typedef uint16_t uint16; +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#else +typedef signed char int8; +typedef unsigned char uint8; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +typedef long long int64; +typedef unsigned long long uint64; +#endif + +typedef std::string string; + +#ifndef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +#if !1 +// Windows does not have an iovec type, yet the concept is universally useful. +// It is simple to define it ourselves, so we put it inside our own namespace. +struct iovec { + void* iov_base; + size_t iov_len; +}; +#endif + +} // namespace snappy + +#endif // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/mac/config.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/mac/config.h new file mode 100644 index 0000000..a844119 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/mac/config.h @@ -0,0 +1,137 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Define to 1 if the compiler supports __builtin_ctz and friends. */ +#define HAVE_BUILTIN_CTZ 1 + +/* Define to 1 if the compiler supports __builtin_expect. */ +#define HAVE_BUILTIN_EXPECT 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_BYTESWAP_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Use the gflags package for command-line parsing. */ +/* #undef HAVE_GFLAGS */ + +/* Defined when Google Test is available. */ +/* #undef HAVE_GTEST */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `fastlz' library (-lfastlz). */ +/* #undef HAVE_LIBFASTLZ */ + +/* Define to 1 if you have the `lzf' library (-llzf). */ +/* #undef HAVE_LIBLZF */ + +/* Define to 1 if you have the `lzo2' library (-llzo2). */ +/* #undef HAVE_LIBLZO2 */ + +/* Define to 1 if you have the `quicklz' library (-lquicklz). */ +/* #undef HAVE_LIBQUICKLZ */ + +/* Define to 1 if you have the `z' library (-lz). */ +#define HAVE_LIBZ 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_BYTESWAP_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_ENDIAN_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "snappy" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "snappy" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "snappy 1.1.4" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "snappy" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.1.4" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "1.1.4" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Define to `int' if does not define. */ +/* #undef ssize_t */ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/mac/snappy-stubs-public.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/mac/snappy-stubs-public.h new file mode 100644 index 0000000..3ce2b59 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/mac/snappy-stubs-public.h @@ -0,0 +1,100 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: sesse@google.com (Steinar H. Gunderson) +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Various type stubs for the open-source version of Snappy. +// +// This file cannot include config.h, as it is included from snappy.h, +// which is a public header. Instead, snappy-stubs-public.h is generated by +// from snappy-stubs-public.h.in at configure time. + +#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ +#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ + +#if 1 +#include +#endif + +#if 1 +#include +#endif + +#if 1 +#include +#endif + +#define SNAPPY_MAJOR 1 +#define SNAPPY_MINOR 1 +#define SNAPPY_PATCHLEVEL 4 +#define SNAPPY_VERSION \ + ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL) + +#include + +namespace snappy { + +#if 1 +typedef int8_t int8; +typedef uint8_t uint8; +typedef int16_t int16; +typedef uint16_t uint16; +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#else +typedef signed char int8; +typedef unsigned char uint8; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +typedef long long int64; +typedef unsigned long long uint64; +#endif + +typedef std::string string; + +#ifndef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +#if !1 +// Windows does not have an iovec type, yet the concept is universally useful. +// It is simple to define it ourselves, so we put it inside our own namespace. +struct iovec { + void* iov_base; + size_t iov_len; +}; +#endif + +} // namespace snappy + +#endif // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/openbsd/config.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/openbsd/config.h new file mode 100644 index 0000000..5ed0ace --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/openbsd/config.h @@ -0,0 +1,135 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Define to 1 if the compiler supports __builtin_ctz and friends. */ +#define HAVE_BUILTIN_CTZ 1 + +/* Define to 1 if the compiler supports __builtin_expect. */ +#define HAVE_BUILTIN_EXPECT 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_BYTESWAP_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Use the gflags package for command-line parsing. */ +/* #undef HAVE_GFLAGS */ + +/* Defined when Google Test is available. */ +/* #undef HAVE_GTEST */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `fastlz' library (-lfastlz). */ +/* #undef HAVE_LIBFASTLZ */ + +/* Define to 1 if you have the `lzf' library (-llzf). */ +/* #undef HAVE_LIBLZF */ + +/* Define to 1 if you have the `lzo2' library (-llzo2). */ +/* #undef HAVE_LIBLZO2 */ + +/* Define to 1 if you have the `quicklz' library (-lquicklz). */ +/* #undef HAVE_LIBQUICKLZ */ + +/* Define to 1 if you have the `z' library (-lz). */ +#define HAVE_LIBZ 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_BYTESWAP_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_ENDIAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "snappy" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "snappy" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "snappy 1.1.4" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "snappy" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.1.4" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "1.1.4" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Define to `int' if does not define. */ +/* #undef ssize_t */ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/openbsd/snappy-stubs-public.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/openbsd/snappy-stubs-public.h new file mode 100644 index 0000000..3ce2b59 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/openbsd/snappy-stubs-public.h @@ -0,0 +1,100 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: sesse@google.com (Steinar H. Gunderson) +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Various type stubs for the open-source version of Snappy. +// +// This file cannot include config.h, as it is included from snappy.h, +// which is a public header. Instead, snappy-stubs-public.h is generated by +// from snappy-stubs-public.h.in at configure time. + +#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ +#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ + +#if 1 +#include +#endif + +#if 1 +#include +#endif + +#if 1 +#include +#endif + +#define SNAPPY_MAJOR 1 +#define SNAPPY_MINOR 1 +#define SNAPPY_PATCHLEVEL 4 +#define SNAPPY_VERSION \ + ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL) + +#include + +namespace snappy { + +#if 1 +typedef int8_t int8; +typedef uint8_t uint8; +typedef int16_t int16; +typedef uint16_t uint16; +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#else +typedef signed char int8; +typedef unsigned char uint8; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +typedef long long int64; +typedef unsigned long long uint64; +#endif + +typedef std::string string; + +#ifndef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +#if !1 +// Windows does not have an iovec type, yet the concept is universally useful. +// It is simple to define it ourselves, so we put it inside our own namespace. +struct iovec { + void* iov_base; + size_t iov_len; +}; +#endif + +} // namespace snappy + +#endif // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy.gyp b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy.gyp new file mode 100644 index 0000000..6af271a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy.gyp @@ -0,0 +1,118 @@ +{ + "variables": { + 'openssl_fips': '' + }, + "targets": [{ + "variables": { + "conditions": [ + ["OS=='linux'", {"os_include": "linux"}], + ["OS=='android'", {"os_include": "linux"}], + ["OS=='ios'", {"os_include": "mac"}], + ["OS=='mac'", {"os_include": "mac"}], + ["OS=='solaris'", {"os_include": "solaris"}], + ["OS=='win'", {"os_include": "win32"}], + ["OS=='freebsd'", {"os_include": "freebsd"}], + ["OS=='openbsd'", {"os_include": "openbsd"}] + ] + }, + "target_name": "snappy", + "type": "static_library", + "standalone_static_library": 1, + "include_dirs": [ + "<(os_include)", + "snappy" + ], + "direct_dependent_settings": { + "include_dirs": [ + "<(os_include)", + "snappy" + ] + }, + "defines": [ + "HAVE_CONFIG_H=1" + ], + "conditions": [ + ["OS == 'win'", { + "defines": [ + "_HAS_EXCEPTIONS=0" + ], + "msvs_settings": { + "VCCLCompilerTool": { + "RuntimeTypeInfo": "false", + "EnableFunctionLevelLinking": "true", + "ExceptionHandling": "2", + "DisableSpecificWarnings": [ "4355", "4530" ,"4267", "4244", "4506", "4018" ] + } + } + }], + ["OS == 'linux'", { + "cflags": [ + "-Wno-sign-compare", + "-Wno-unused-function" + ], + "cflags!": [ "-fno-tree-vrp" ] + }], + ["OS == 'freebsd'", { + "cflags": [ + "-Wno-sign-compare", + "-Wno-unused-function" + ] + }], + ["OS == 'openbsd'", { + "cflags": [ + "-Wno-sign-compare", + "-Wno-unused-function" + ] + }], + ["OS == 'solaris'", { + "cflags": [ + "-Wno-sign-compare", + "-Wno-unused-function" + ] + }], + ["OS == 'mac'", { + "xcode_settings": { + "WARNING_CFLAGS": [ + "-Wno-sign-compare", + "-Wno-unused-function" + ], + + # Set minimum target version because we're building on newer + "MACOSX_DEPLOYMENT_TARGET": "10.7", + + # Build universal binary to support M1 (Apple silicon) + "OTHER_CFLAGS": [ + "-arch x86_64", + "-arch arm64" + ] + } + }], + ["OS == 'android'", { + "cflags": [ + "-Wno-sign-compare", + "-fPIC", + "-Wno-unused-function" + ], + "cflags!": [ + "-fno-tree-vrp", + "-mfloat-abi=hard", + "-fPIE" + ] + }], + ["target_arch == 'arm'", { + "cflags": [ + "-mfloat-abi=hard" + ] + }] + ], + "sources": [ + "snappy/snappy-internal.h", + "snappy/snappy-sinksource.cc", + "snappy/snappy-sinksource.h", + "snappy/snappy-stubs-internal.cc", + "snappy/snappy-stubs-internal.h", + "snappy/snappy.cc", + "snappy/snappy.h" + ] + }] +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/COPYING b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/COPYING new file mode 100644 index 0000000..bd0e597 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/COPYING @@ -0,0 +1,54 @@ +Copyright 2011, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=== + +Some of the benchmark data in testdata/ is licensed differently: + + - fireworks.jpeg is Copyright 2013 Steinar H. Gunderson, and + is licensed under the Creative Commons Attribution 3.0 license + (CC-BY-3.0). See https://creativecommons.org/licenses/by/3.0/ + for more information. + + - kppkn.gtb is taken from the Gaviota chess tablebase set, and + is licensed under the MIT License. See + https://sites.google.com/site/gaviotachessengine/Home/endgame-tablebases-1 + for more information. + + - paper-100k.pdf is an excerpt (bytes 92160 to 194560) from the paper + “Combinatorial Modeling of Chromatin Features Quantitatively Predicts DNA + Replication Timing in _Drosophila_” by Federico Comoglio and Renato Paro, + which is licensed under the CC-BY license. See + http://www.ploscompbiol.org/static/license for more ifnormation. + + - alice29.txt, asyoulik.txt, plrabn12.txt and lcet10.txt are from Project + Gutenberg. The first three have expired copyrights and are in the public + domain; the latter does not have expired copyright, but is still in the + public domain according to the license information + (http://www.gutenberg.org/ebooks/53). diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/README.md new file mode 100644 index 0000000..b9db833 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/README.md @@ -0,0 +1,149 @@ +Snappy, a fast compressor/decompressor. + + +Introduction +============ + +Snappy is a compression/decompression library. It does not aim for maximum +compression, or compatibility with any other compression library; instead, +it aims for very high speeds and reasonable compression. For instance, +compared to the fastest mode of zlib, Snappy is an order of magnitude faster +for most inputs, but the resulting compressed files are anywhere from 20% to +100% bigger. (For more information, see "Performance", below.) + +Snappy has the following properties: + + * Fast: Compression speeds at 250 MB/sec and beyond, with no assembler code. + See "Performance" below. + * Stable: Over the last few years, Snappy has compressed and decompressed + petabytes of data in Google's production environment. The Snappy bitstream + format is stable and will not change between versions. + * Robust: The Snappy decompressor is designed not to crash in the face of + corrupted or malicious input. + * Free and open source software: Snappy is licensed under a BSD-type license. + For more information, see the included COPYING file. + +Snappy has previously been called "Zippy" in some Google presentations +and the like. + + +Performance +=========== + +Snappy is intended to be fast. On a single core of a Core i7 processor +in 64-bit mode, it compresses at about 250 MB/sec or more and decompresses at +about 500 MB/sec or more. (These numbers are for the slowest inputs in our +benchmark suite; others are much faster.) In our tests, Snappy usually +is faster than algorithms in the same class (e.g. LZO, LZF, QuickLZ, +etc.) while achieving comparable compression ratios. + +Typical compression ratios (based on the benchmark suite) are about 1.5-1.7x +for plain text, about 2-4x for HTML, and of course 1.0x for JPEGs, PNGs and +other already-compressed data. Similar numbers for zlib in its fastest mode +are 2.6-2.8x, 3-7x and 1.0x, respectively. More sophisticated algorithms are +capable of achieving yet higher compression rates, although usually at the +expense of speed. Of course, compression ratio will vary significantly with +the input. + +Although Snappy should be fairly portable, it is primarily optimized +for 64-bit x86-compatible processors, and may run slower in other environments. +In particular: + + - Snappy uses 64-bit operations in several places to process more data at + once than would otherwise be possible. + - Snappy assumes unaligned 32- and 64-bit loads and stores are cheap. + On some platforms, these must be emulated with single-byte loads + and stores, which is much slower. + - Snappy assumes little-endian throughout, and needs to byte-swap data in + several places if running on a big-endian platform. + +Experience has shown that even heavily tuned code can be improved. +Performance optimizations, whether for 64-bit x86 or other platforms, +are of course most welcome; see "Contact", below. + + +Building +======== + +CMake is supported and autotools will soon be deprecated. +You need CMake 3.4 or above to build: + + mkdir build + cd build && cmake ../ && make + + +Usage +===== + +Note that Snappy, both the implementation and the main interface, +is written in C++. However, several third-party bindings to other languages +are available; see the home page at http://google.github.io/snappy/ +for more information. Also, if you want to use Snappy from C code, you can +use the included C bindings in snappy-c.h. + +To use Snappy from your own C++ program, include the file "snappy.h" from +your calling file, and link against the compiled library. + +There are many ways to call Snappy, but the simplest possible is + + snappy::Compress(input.data(), input.size(), &output); + +and similarly + + snappy::Uncompress(input.data(), input.size(), &output); + +where "input" and "output" are both instances of std::string. + +There are other interfaces that are more flexible in various ways, including +support for custom (non-array) input sources. See the header file for more +information. + + +Tests and benchmarks +==================== + +When you compile Snappy, snappy_unittest is compiled in addition to the +library itself. You do not need it to use the compressor from your own library, +but it contains several useful components for Snappy development. + +First of all, it contains unit tests, verifying correctness on your machine in +various scenarios. If you want to change or optimize Snappy, please run the +tests to verify you have not broken anything. Note that if you have the +Google Test library installed, unit test behavior (especially failures) will be +significantly more user-friendly. You can find Google Test at + + http://github.com/google/googletest + +You probably also want the gflags library for handling of command-line flags; +you can find it at + + http://gflags.github.io/gflags/ + +In addition to the unit tests, snappy contains microbenchmarks used to +tune compression and decompression performance. These are automatically run +before the unit tests, but you can disable them using the flag +--run_microbenchmarks=false if you have gflags installed (otherwise you will +need to edit the source). + +Finally, snappy can benchmark Snappy against a few other compression libraries +(zlib, LZO, LZF, and QuickLZ), if they were detected at configure time. +To benchmark using a given file, give the compression algorithm you want to test +Snappy against (e.g. --zlib) and then a list of one or more file names on the +command line. The testdata/ directory contains the files used by the +microbenchmark, which should provide a reasonably balanced starting point for +benchmarking. (Note that baddata[1-3].snappy are not intended as benchmarks; they +are used to verify correctness in the presence of corrupted data in the unit +test.) + + +Contact +======= + +Snappy is distributed through GitHub. For the latest version, a bug tracker, +and other information, see + + http://google.github.io/snappy/ + +or the repository at + + https://github.com/google/snappy diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/cmake/SnappyConfig.cmake b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/cmake/SnappyConfig.cmake new file mode 100644 index 0000000..dc308ae --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/cmake/SnappyConfig.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/SnappyTargets.cmake") \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/cmake/config.h.in b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/cmake/config.h.in new file mode 100644 index 0000000..97cd818 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/cmake/config.h.in @@ -0,0 +1,62 @@ +#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_CMAKE_CONFIG_H_ +#define THIRD_PARTY_SNAPPY_OPENSOURCE_CMAKE_CONFIG_H_ + +/* Define to 1 if the compiler supports __builtin_ctz and friends. */ +#cmakedefine HAVE_BUILTIN_CTZ 1 + +/* Define to 1 if the compiler supports __builtin_expect. */ +#cmakedefine HAVE_BUILTIN_EXPECT 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_BYTESWAP_H 1 + +/* Define to 1 if you have a definition for mmap() in . */ +#cmakedefine HAVE_FUNC_MMAP 1 + +/* Define to 1 if you have a definition for sysconf() in . */ +#cmakedefine HAVE_FUNC_SYSCONF 1 + +/* Define to 1 to use the gflags package for command-line parsing. */ +#cmakedefine HAVE_GFLAGS 1 + +/* Define to 1 if you have Google Test. */ +#cmakedefine HAVE_GTEST 1 + +/* Define to 1 if you have the `lzo2' library (-llzo2). */ +#cmakedefine HAVE_LIBLZO2 1 + +/* Define to 1 if you have the `z' library (-lz). */ +#cmakedefine HAVE_LIBZ 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_ENDIAN_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_WINDOWS_H 1 + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#cmakedefine SNAPPY_IS_BIG_ENDIAN 1 + +#endif // THIRD_PARTY_SNAPPY_OPENSOURCE_CMAKE_CONFIG_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-c.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-c.cc new file mode 100644 index 0000000..473a0b0 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-c.cc @@ -0,0 +1,90 @@ +// Copyright 2011 Martin Gieseking . +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "snappy.h" +#include "snappy-c.h" + +extern "C" { + +snappy_status snappy_compress(const char* input, + size_t input_length, + char* compressed, + size_t *compressed_length) { + if (*compressed_length < snappy_max_compressed_length(input_length)) { + return SNAPPY_BUFFER_TOO_SMALL; + } + snappy::RawCompress(input, input_length, compressed, compressed_length); + return SNAPPY_OK; +} + +snappy_status snappy_uncompress(const char* compressed, + size_t compressed_length, + char* uncompressed, + size_t* uncompressed_length) { + size_t real_uncompressed_length; + if (!snappy::GetUncompressedLength(compressed, + compressed_length, + &real_uncompressed_length)) { + return SNAPPY_INVALID_INPUT; + } + if (*uncompressed_length < real_uncompressed_length) { + return SNAPPY_BUFFER_TOO_SMALL; + } + if (!snappy::RawUncompress(compressed, compressed_length, uncompressed)) { + return SNAPPY_INVALID_INPUT; + } + *uncompressed_length = real_uncompressed_length; + return SNAPPY_OK; +} + +size_t snappy_max_compressed_length(size_t source_length) { + return snappy::MaxCompressedLength(source_length); +} + +snappy_status snappy_uncompressed_length(const char *compressed, + size_t compressed_length, + size_t *result) { + if (snappy::GetUncompressedLength(compressed, + compressed_length, + result)) { + return SNAPPY_OK; + } else { + return SNAPPY_INVALID_INPUT; + } +} + +snappy_status snappy_validate_compressed_buffer(const char *compressed, + size_t compressed_length) { + if (snappy::IsValidCompressedBuffer(compressed, compressed_length)) { + return SNAPPY_OK; + } else { + return SNAPPY_INVALID_INPUT; + } +} + +} // extern "C" diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-c.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-c.h new file mode 100644 index 0000000..32aa0c6 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-c.h @@ -0,0 +1,138 @@ +/* + * Copyright 2011 Martin Gieseking . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Plain C interface (a wrapper around the C++ implementation). + */ + +#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_C_H_ +#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + * Return values; see the documentation for each function to know + * what each can return. + */ +typedef enum { + SNAPPY_OK = 0, + SNAPPY_INVALID_INPUT = 1, + SNAPPY_BUFFER_TOO_SMALL = 2 +} snappy_status; + +/* + * Takes the data stored in "input[0..input_length-1]" and stores + * it in the array pointed to by "compressed". + * + * signals the space available in "compressed". + * If it is not at least equal to "snappy_max_compressed_length(input_length)", + * SNAPPY_BUFFER_TOO_SMALL is returned. After successful compression, + * contains the true length of the compressed output, + * and SNAPPY_OK is returned. + * + * Example: + * size_t output_length = snappy_max_compressed_length(input_length); + * char* output = (char*)malloc(output_length); + * if (snappy_compress(input, input_length, output, &output_length) + * == SNAPPY_OK) { + * ... Process(output, output_length) ... + * } + * free(output); + */ +snappy_status snappy_compress(const char* input, + size_t input_length, + char* compressed, + size_t* compressed_length); + +/* + * Given data in "compressed[0..compressed_length-1]" generated by + * calling the snappy_compress routine, this routine stores + * the uncompressed data to + * uncompressed[0..uncompressed_length-1]. + * Returns failure (a value not equal to SNAPPY_OK) if the message + * is corrupted and could not be decrypted. + * + * signals the space available in "uncompressed". + * If it is not at least equal to the value returned by + * snappy_uncompressed_length for this stream, SNAPPY_BUFFER_TOO_SMALL + * is returned. After successful decompression, + * contains the true length of the decompressed output. + * + * Example: + * size_t output_length; + * if (snappy_uncompressed_length(input, input_length, &output_length) + * != SNAPPY_OK) { + * ... fail ... + * } + * char* output = (char*)malloc(output_length); + * if (snappy_uncompress(input, input_length, output, &output_length) + * == SNAPPY_OK) { + * ... Process(output, output_length) ... + * } + * free(output); + */ +snappy_status snappy_uncompress(const char* compressed, + size_t compressed_length, + char* uncompressed, + size_t* uncompressed_length); + +/* + * Returns the maximal size of the compressed representation of + * input data that is "source_length" bytes in length. + */ +size_t snappy_max_compressed_length(size_t source_length); + +/* + * REQUIRES: "compressed[]" was produced by snappy_compress() + * Returns SNAPPY_OK and stores the length of the uncompressed data in + * *result normally. Returns SNAPPY_INVALID_INPUT on parsing error. + * This operation takes O(1) time. + */ +snappy_status snappy_uncompressed_length(const char* compressed, + size_t compressed_length, + size_t* result); + +/* + * Check if the contents of "compressed[]" can be uncompressed successfully. + * Does not return the uncompressed data; if so, returns SNAPPY_OK, + * or if not, returns SNAPPY_INVALID_INPUT. + * Takes time proportional to compressed_length, but is usually at least a + * factor of four faster than actual decompression. + */ +snappy_status snappy_validate_compressed_buffer(const char* compressed, + size_t compressed_length); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_C_H_ */ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-internal.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-internal.h new file mode 100644 index 0000000..4b53d59 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-internal.h @@ -0,0 +1,224 @@ +// Copyright 2008 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Internals shared between the Snappy implementation and its unittest. + +#ifndef THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_ +#define THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_ + +#include "snappy-stubs-internal.h" + +namespace snappy { +namespace internal { + +class WorkingMemory { + public: + WorkingMemory() : large_table_(NULL) { } + ~WorkingMemory() { delete[] large_table_; } + + // Allocates and clears a hash table using memory in "*this", + // stores the number of buckets in "*table_size" and returns a pointer to + // the base of the hash table. + uint16* GetHashTable(size_t input_size, int* table_size); + + private: + uint16 small_table_[1<<10]; // 2KB + uint16* large_table_; // Allocated only when needed + + // No copying + WorkingMemory(const WorkingMemory&); + void operator=(const WorkingMemory&); +}; + +// Flat array compression that does not emit the "uncompressed length" +// prefix. Compresses "input" string to the "*op" buffer. +// +// REQUIRES: "input_length <= kBlockSize" +// REQUIRES: "op" points to an array of memory that is at least +// "MaxCompressedLength(input_length)" in size. +// REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. +// REQUIRES: "table_size" is a power of two +// +// Returns an "end" pointer into "op" buffer. +// "end - op" is the compressed size of "input". +char* CompressFragment(const char* input, + size_t input_length, + char* op, + uint16* table, + const int table_size); + +// Find the largest n such that +// +// s1[0,n-1] == s2[0,n-1] +// and n <= (s2_limit - s2). +// +// Return make_pair(n, n < 8). +// Does not read *s2_limit or beyond. +// Does not read *(s1 + (s2_limit - s2)) or beyond. +// Requires that s2_limit >= s2. +// +// Separate implementation for 64-bit, little-endian cpus. +#if !defined(SNAPPY_IS_BIG_ENDIAN) && \ + (defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM)) +static inline std::pair FindMatchLength(const char* s1, + const char* s2, + const char* s2_limit) { + assert(s2_limit >= s2); + size_t matched = 0; + + // This block isn't necessary for correctness; we could just start looping + // immediately. As an optimization though, it is useful. It creates some not + // uncommon code paths that determine, without extra effort, whether the match + // length is less than 8. In short, we are hoping to avoid a conditional + // branch, and perhaps get better code layout from the C++ compiler. + if (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 8)) { + uint64 a1 = UNALIGNED_LOAD64(s1); + uint64 a2 = UNALIGNED_LOAD64(s2); + if (a1 != a2) { + return std::pair(Bits::FindLSBSetNonZero64(a1 ^ a2) >> 3, + true); + } else { + matched = 8; + s2 += 8; + } + } + + // Find out how long the match is. We loop over the data 64 bits at a + // time until we find a 64-bit block that doesn't match; then we find + // the first non-matching bit and use that to calculate the total + // length of the match. + while (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 8)) { + if (UNALIGNED_LOAD64(s2) == UNALIGNED_LOAD64(s1 + matched)) { + s2 += 8; + matched += 8; + } else { + uint64 x = UNALIGNED_LOAD64(s2) ^ UNALIGNED_LOAD64(s1 + matched); + int matching_bits = Bits::FindLSBSetNonZero64(x); + matched += matching_bits >> 3; + assert(matched >= 8); + return std::pair(matched, false); + } + } + while (SNAPPY_PREDICT_TRUE(s2 < s2_limit)) { + if (s1[matched] == *s2) { + ++s2; + ++matched; + } else { + return std::pair(matched, matched < 8); + } + } + return std::pair(matched, matched < 8); +} +#else +static inline std::pair FindMatchLength(const char* s1, + const char* s2, + const char* s2_limit) { + // Implementation based on the x86-64 version, above. + assert(s2_limit >= s2); + int matched = 0; + + while (s2 <= s2_limit - 4 && + UNALIGNED_LOAD32(s2) == UNALIGNED_LOAD32(s1 + matched)) { + s2 += 4; + matched += 4; + } + if (LittleEndian::IsLittleEndian() && s2 <= s2_limit - 4) { + uint32 x = UNALIGNED_LOAD32(s2) ^ UNALIGNED_LOAD32(s1 + matched); + int matching_bits = Bits::FindLSBSetNonZero(x); + matched += matching_bits >> 3; + } else { + while ((s2 < s2_limit) && (s1[matched] == *s2)) { + ++s2; + ++matched; + } + } + return std::pair(matched, matched < 8); +} +#endif + +// Lookup tables for decompression code. Give --snappy_dump_decompression_table +// to the unit test to recompute char_table. + +enum { + LITERAL = 0, + COPY_1_BYTE_OFFSET = 1, // 3 bit length + 3 bits of offset in opcode + COPY_2_BYTE_OFFSET = 2, + COPY_4_BYTE_OFFSET = 3 +}; +static const int kMaximumTagLength = 5; // COPY_4_BYTE_OFFSET plus the actual offset. + +// Data stored per entry in lookup table: +// Range Bits-used Description +// ------------------------------------ +// 1..64 0..7 Literal/copy length encoded in opcode byte +// 0..7 8..10 Copy offset encoded in opcode byte / 256 +// 0..4 11..13 Extra bytes after opcode +// +// We use eight bits for the length even though 7 would have sufficed +// because of efficiency reasons: +// (1) Extracting a byte is faster than a bit-field +// (2) It properly aligns copy offset so we do not need a <<8 +static const uint16 char_table[256] = { + 0x0001, 0x0804, 0x1001, 0x2001, 0x0002, 0x0805, 0x1002, 0x2002, + 0x0003, 0x0806, 0x1003, 0x2003, 0x0004, 0x0807, 0x1004, 0x2004, + 0x0005, 0x0808, 0x1005, 0x2005, 0x0006, 0x0809, 0x1006, 0x2006, + 0x0007, 0x080a, 0x1007, 0x2007, 0x0008, 0x080b, 0x1008, 0x2008, + 0x0009, 0x0904, 0x1009, 0x2009, 0x000a, 0x0905, 0x100a, 0x200a, + 0x000b, 0x0906, 0x100b, 0x200b, 0x000c, 0x0907, 0x100c, 0x200c, + 0x000d, 0x0908, 0x100d, 0x200d, 0x000e, 0x0909, 0x100e, 0x200e, + 0x000f, 0x090a, 0x100f, 0x200f, 0x0010, 0x090b, 0x1010, 0x2010, + 0x0011, 0x0a04, 0x1011, 0x2011, 0x0012, 0x0a05, 0x1012, 0x2012, + 0x0013, 0x0a06, 0x1013, 0x2013, 0x0014, 0x0a07, 0x1014, 0x2014, + 0x0015, 0x0a08, 0x1015, 0x2015, 0x0016, 0x0a09, 0x1016, 0x2016, + 0x0017, 0x0a0a, 0x1017, 0x2017, 0x0018, 0x0a0b, 0x1018, 0x2018, + 0x0019, 0x0b04, 0x1019, 0x2019, 0x001a, 0x0b05, 0x101a, 0x201a, + 0x001b, 0x0b06, 0x101b, 0x201b, 0x001c, 0x0b07, 0x101c, 0x201c, + 0x001d, 0x0b08, 0x101d, 0x201d, 0x001e, 0x0b09, 0x101e, 0x201e, + 0x001f, 0x0b0a, 0x101f, 0x201f, 0x0020, 0x0b0b, 0x1020, 0x2020, + 0x0021, 0x0c04, 0x1021, 0x2021, 0x0022, 0x0c05, 0x1022, 0x2022, + 0x0023, 0x0c06, 0x1023, 0x2023, 0x0024, 0x0c07, 0x1024, 0x2024, + 0x0025, 0x0c08, 0x1025, 0x2025, 0x0026, 0x0c09, 0x1026, 0x2026, + 0x0027, 0x0c0a, 0x1027, 0x2027, 0x0028, 0x0c0b, 0x1028, 0x2028, + 0x0029, 0x0d04, 0x1029, 0x2029, 0x002a, 0x0d05, 0x102a, 0x202a, + 0x002b, 0x0d06, 0x102b, 0x202b, 0x002c, 0x0d07, 0x102c, 0x202c, + 0x002d, 0x0d08, 0x102d, 0x202d, 0x002e, 0x0d09, 0x102e, 0x202e, + 0x002f, 0x0d0a, 0x102f, 0x202f, 0x0030, 0x0d0b, 0x1030, 0x2030, + 0x0031, 0x0e04, 0x1031, 0x2031, 0x0032, 0x0e05, 0x1032, 0x2032, + 0x0033, 0x0e06, 0x1033, 0x2033, 0x0034, 0x0e07, 0x1034, 0x2034, + 0x0035, 0x0e08, 0x1035, 0x2035, 0x0036, 0x0e09, 0x1036, 0x2036, + 0x0037, 0x0e0a, 0x1037, 0x2037, 0x0038, 0x0e0b, 0x1038, 0x2038, + 0x0039, 0x0f04, 0x1039, 0x2039, 0x003a, 0x0f05, 0x103a, 0x203a, + 0x003b, 0x0f06, 0x103b, 0x203b, 0x003c, 0x0f07, 0x103c, 0x203c, + 0x0801, 0x0f08, 0x103d, 0x203d, 0x1001, 0x0f09, 0x103e, 0x203e, + 0x1801, 0x0f0a, 0x103f, 0x203f, 0x2001, 0x0f0b, 0x1040, 0x2040 +}; + +} // end namespace internal +} // end namespace snappy + +#endif // THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-sinksource.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-sinksource.cc new file mode 100644 index 0000000..369a132 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-sinksource.cc @@ -0,0 +1,104 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "snappy-sinksource.h" + +namespace snappy { + +Source::~Source() { } + +Sink::~Sink() { } + +char* Sink::GetAppendBuffer(size_t length, char* scratch) { + return scratch; +} + +char* Sink::GetAppendBufferVariable( + size_t min_size, size_t desired_size_hint, char* scratch, + size_t scratch_size, size_t* allocated_size) { + *allocated_size = scratch_size; + return scratch; +} + +void Sink::AppendAndTakeOwnership( + char* bytes, size_t n, + void (*deleter)(void*, const char*, size_t), + void *deleter_arg) { + Append(bytes, n); + (*deleter)(deleter_arg, bytes, n); +} + +ByteArraySource::~ByteArraySource() { } + +size_t ByteArraySource::Available() const { return left_; } + +const char* ByteArraySource::Peek(size_t* len) { + *len = left_; + return ptr_; +} + +void ByteArraySource::Skip(size_t n) { + left_ -= n; + ptr_ += n; +} + +UncheckedByteArraySink::~UncheckedByteArraySink() { } + +void UncheckedByteArraySink::Append(const char* data, size_t n) { + // Do no copying if the caller filled in the result of GetAppendBuffer() + if (data != dest_) { + memcpy(dest_, data, n); + } + dest_ += n; +} + +char* UncheckedByteArraySink::GetAppendBuffer(size_t len, char* scratch) { + return dest_; +} + +void UncheckedByteArraySink::AppendAndTakeOwnership( + char* data, size_t n, + void (*deleter)(void*, const char*, size_t), + void *deleter_arg) { + if (data != dest_) { + memcpy(dest_, data, n); + (*deleter)(deleter_arg, data, n); + } + dest_ += n; +} + +char* UncheckedByteArraySink::GetAppendBufferVariable( + size_t min_size, size_t desired_size_hint, char* scratch, + size_t scratch_size, size_t* allocated_size) { + *allocated_size = desired_size_hint; + return dest_; +} + +} // namespace snappy diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-sinksource.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-sinksource.h new file mode 100644 index 0000000..8afcdaa --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-sinksource.h @@ -0,0 +1,182 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef THIRD_PARTY_SNAPPY_SNAPPY_SINKSOURCE_H_ +#define THIRD_PARTY_SNAPPY_SNAPPY_SINKSOURCE_H_ + +#include + +namespace snappy { + +// A Sink is an interface that consumes a sequence of bytes. +class Sink { + public: + Sink() { } + virtual ~Sink(); + + // Append "bytes[0,n-1]" to this. + virtual void Append(const char* bytes, size_t n) = 0; + + // Returns a writable buffer of the specified length for appending. + // May return a pointer to the caller-owned scratch buffer which + // must have at least the indicated length. The returned buffer is + // only valid until the next operation on this Sink. + // + // After writing at most "length" bytes, call Append() with the + // pointer returned from this function and the number of bytes + // written. Many Append() implementations will avoid copying + // bytes if this function returned an internal buffer. + // + // If a non-scratch buffer is returned, the caller may only pass a + // prefix of it to Append(). That is, it is not correct to pass an + // interior pointer of the returned array to Append(). + // + // The default implementation always returns the scratch buffer. + virtual char* GetAppendBuffer(size_t length, char* scratch); + + // For higher performance, Sink implementations can provide custom + // AppendAndTakeOwnership() and GetAppendBufferVariable() methods. + // These methods can reduce the number of copies done during + // compression/decompression. + + // Append "bytes[0,n-1] to the sink. Takes ownership of "bytes" + // and calls the deleter function as (*deleter)(deleter_arg, bytes, n) + // to free the buffer. deleter function must be non NULL. + // + // The default implementation just calls Append and frees "bytes". + // Other implementations may avoid a copy while appending the buffer. + virtual void AppendAndTakeOwnership( + char* bytes, size_t n, void (*deleter)(void*, const char*, size_t), + void *deleter_arg); + + // Returns a writable buffer for appending and writes the buffer's capacity to + // *allocated_size. Guarantees *allocated_size >= min_size. + // May return a pointer to the caller-owned scratch buffer which must have + // scratch_size >= min_size. + // + // The returned buffer is only valid until the next operation + // on this ByteSink. + // + // After writing at most *allocated_size bytes, call Append() with the + // pointer returned from this function and the number of bytes written. + // Many Append() implementations will avoid copying bytes if this function + // returned an internal buffer. + // + // If the sink implementation allocates or reallocates an internal buffer, + // it should use the desired_size_hint if appropriate. If a caller cannot + // provide a reasonable guess at the desired capacity, it should set + // desired_size_hint = 0. + // + // If a non-scratch buffer is returned, the caller may only pass + // a prefix to it to Append(). That is, it is not correct to pass an + // interior pointer to Append(). + // + // The default implementation always returns the scratch buffer. + virtual char* GetAppendBufferVariable( + size_t min_size, size_t desired_size_hint, char* scratch, + size_t scratch_size, size_t* allocated_size); + + private: + // No copying + Sink(const Sink&); + void operator=(const Sink&); +}; + +// A Source is an interface that yields a sequence of bytes +class Source { + public: + Source() { } + virtual ~Source(); + + // Return the number of bytes left to read from the source + virtual size_t Available() const = 0; + + // Peek at the next flat region of the source. Does not reposition + // the source. The returned region is empty iff Available()==0. + // + // Returns a pointer to the beginning of the region and store its + // length in *len. + // + // The returned region is valid until the next call to Skip() or + // until this object is destroyed, whichever occurs first. + // + // The returned region may be larger than Available() (for example + // if this ByteSource is a view on a substring of a larger source). + // The caller is responsible for ensuring that it only reads the + // Available() bytes. + virtual const char* Peek(size_t* len) = 0; + + // Skip the next n bytes. Invalidates any buffer returned by + // a previous call to Peek(). + // REQUIRES: Available() >= n + virtual void Skip(size_t n) = 0; + + private: + // No copying + Source(const Source&); + void operator=(const Source&); +}; + +// A Source implementation that yields the contents of a flat array +class ByteArraySource : public Source { + public: + ByteArraySource(const char* p, size_t n) : ptr_(p), left_(n) { } + virtual ~ByteArraySource(); + virtual size_t Available() const; + virtual const char* Peek(size_t* len); + virtual void Skip(size_t n); + private: + const char* ptr_; + size_t left_; +}; + +// A Sink implementation that writes to a flat array without any bound checks. +class UncheckedByteArraySink : public Sink { + public: + explicit UncheckedByteArraySink(char* dest) : dest_(dest) { } + virtual ~UncheckedByteArraySink(); + virtual void Append(const char* data, size_t n); + virtual char* GetAppendBuffer(size_t len, char* scratch); + virtual char* GetAppendBufferVariable( + size_t min_size, size_t desired_size_hint, char* scratch, + size_t scratch_size, size_t* allocated_size); + virtual void AppendAndTakeOwnership( + char* bytes, size_t n, void (*deleter)(void*, const char*, size_t), + void *deleter_arg); + + // Return the current output pointer so that a caller can see how + // many bytes were produced. + // Note: this is not a Sink method. + char* CurrentDestination() const { return dest_; } + private: + char* dest_; +}; + +} // namespace snappy + +#endif // THIRD_PARTY_SNAPPY_SNAPPY_SINKSOURCE_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-stubs-internal.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-stubs-internal.cc new file mode 100644 index 0000000..6ed3343 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-stubs-internal.cc @@ -0,0 +1,42 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include + +#include "snappy-stubs-internal.h" + +namespace snappy { + +void Varint::Append32(string* s, uint32 value) { + char buf[Varint::kMax32]; + const char* p = Varint::Encode32(buf, value); + s->append(buf, p - buf); +} + +} // namespace snappy diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-stubs-internal.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-stubs-internal.h new file mode 100644 index 0000000..cb605f8 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-stubs-internal.h @@ -0,0 +1,561 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Various stubs for the open-source version of Snappy. + +#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ +#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#if defined(_MSC_VER) +#include +#endif // defined(_MSC_VER) + +#include "snappy-stubs-public.h" + +#if defined(__x86_64__) + +// Enable 64-bit optimized versions of some routines. +#define ARCH_K8 1 + +#elif defined(__ppc64__) + +#define ARCH_PPC 1 + +#elif defined(__aarch64__) + +#define ARCH_ARM 1 + +#endif + +// Needed by OS X, among others. +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + +// The size of an array, if known at compile-time. +// Will give unexpected results if used on a pointer. +// We undefine it first, since some compilers already have a definition. +#ifdef ARRAYSIZE +#undef ARRAYSIZE +#endif +#define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a))) + +// Static prediction hints. +#ifdef HAVE_BUILTIN_EXPECT +#define SNAPPY_PREDICT_FALSE(x) (__builtin_expect(x, 0)) +#define SNAPPY_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#else +#define SNAPPY_PREDICT_FALSE(x) x +#define SNAPPY_PREDICT_TRUE(x) x +#endif + +// This is only used for recomputing the tag byte table used during +// decompression; for simplicity we just remove it from the open-source +// version (anyone who wants to regenerate it can just do the call +// themselves within main()). +#define DEFINE_bool(flag_name, default_value, description) \ + bool FLAGS_ ## flag_name = default_value +#define DECLARE_bool(flag_name) \ + extern bool FLAGS_ ## flag_name + +namespace snappy { + +static const uint32 kuint32max = static_cast(0xFFFFFFFF); +static const int64 kint64max = static_cast(0x7FFFFFFFFFFFFFFFLL); + +// Potentially unaligned loads and stores. + +// x86, PowerPC, and ARM64 can simply do these loads and stores native. + +#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) || \ + defined(__aarch64__) + +#define UNALIGNED_LOAD16(_p) (*reinterpret_cast(_p)) +#define UNALIGNED_LOAD32(_p) (*reinterpret_cast(_p)) +#define UNALIGNED_LOAD64(_p) (*reinterpret_cast(_p)) + +#define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast(_p) = (_val)) +#define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast(_p) = (_val)) +#define UNALIGNED_STORE64(_p, _val) (*reinterpret_cast(_p) = (_val)) + +// ARMv7 and newer support native unaligned accesses, but only of 16-bit +// and 32-bit values (not 64-bit); older versions either raise a fatal signal, +// do an unaligned read and rotate the words around a bit, or do the reads very +// slowly (trip through kernel mode). There's no simple #define that says just +// “ARMv7 or higher”, so we have to filter away all ARMv5 and ARMv6 +// sub-architectures. +// +// This is a mess, but there's not much we can do about it. +// +// To further complicate matters, only LDR instructions (single reads) are +// allowed to be unaligned, not LDRD (two reads) or LDM (many reads). Unless we +// explicitly tell the compiler that these accesses can be unaligned, it can and +// will combine accesses. On armcc, the way to signal this is done by accessing +// through the type (uint32 __packed *), but GCC has no such attribute +// (it ignores __attribute__((packed)) on individual variables). However, +// we can tell it that a _struct_ is unaligned, which has the same effect, +// so we do that. + +#elif defined(__arm__) && \ + !defined(__ARM_ARCH_4__) && \ + !defined(__ARM_ARCH_4T__) && \ + !defined(__ARM_ARCH_5__) && \ + !defined(__ARM_ARCH_5T__) && \ + !defined(__ARM_ARCH_5TE__) && \ + !defined(__ARM_ARCH_5TEJ__) && \ + !defined(__ARM_ARCH_6__) && \ + !defined(__ARM_ARCH_6J__) && \ + !defined(__ARM_ARCH_6K__) && \ + !defined(__ARM_ARCH_6Z__) && \ + !defined(__ARM_ARCH_6ZK__) && \ + !defined(__ARM_ARCH_6T2__) + +#if __GNUC__ +#define ATTRIBUTE_PACKED __attribute__((__packed__)) +#else +#define ATTRIBUTE_PACKED +#endif + +namespace base { +namespace internal { + +struct Unaligned16Struct { + uint16 value; + uint8 dummy; // To make the size non-power-of-two. +} ATTRIBUTE_PACKED; + +struct Unaligned32Struct { + uint32 value; + uint8 dummy; // To make the size non-power-of-two. +} ATTRIBUTE_PACKED; + +} // namespace internal +} // namespace base + +#define UNALIGNED_LOAD16(_p) \ + ((reinterpret_cast(_p))->value) +#define UNALIGNED_LOAD32(_p) \ + ((reinterpret_cast(_p))->value) + +#define UNALIGNED_STORE16(_p, _val) \ + ((reinterpret_cast< ::snappy::base::internal::Unaligned16Struct *>(_p))->value = \ + (_val)) +#define UNALIGNED_STORE32(_p, _val) \ + ((reinterpret_cast< ::snappy::base::internal::Unaligned32Struct *>(_p))->value = \ + (_val)) + +// TODO(user): NEON supports unaligned 64-bit loads and stores. +// See if that would be more efficient on platforms supporting it, +// at least for copies. + +inline uint64 UNALIGNED_LOAD64(const void *p) { + uint64 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline void UNALIGNED_STORE64(void *p, uint64 v) { + memcpy(p, &v, sizeof v); +} + +#else + +// These functions are provided for architectures that don't support +// unaligned loads and stores. + +inline uint16 UNALIGNED_LOAD16(const void *p) { + uint16 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint32 UNALIGNED_LOAD32(const void *p) { + uint32 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint64 UNALIGNED_LOAD64(const void *p) { + uint64 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline void UNALIGNED_STORE16(void *p, uint16 v) { + memcpy(p, &v, sizeof v); +} + +inline void UNALIGNED_STORE32(void *p, uint32 v) { + memcpy(p, &v, sizeof v); +} + +inline void UNALIGNED_STORE64(void *p, uint64 v) { + memcpy(p, &v, sizeof v); +} + +#endif + +// The following guarantees declaration of the byte swap functions. +#if defined(SNAPPY_IS_BIG_ENDIAN) + +#ifdef HAVE_SYS_BYTEORDER_H +#include +#endif + +#ifdef HAVE_SYS_ENDIAN_H +#include +#endif + +#ifdef _MSC_VER +#include +#define bswap_16(x) _byteswap_ushort(x) +#define bswap_32(x) _byteswap_ulong(x) +#define bswap_64(x) _byteswap_uint64(x) + +#elif defined(__APPLE__) +// Mac OS X / Darwin features +#include +#define bswap_16(x) OSSwapInt16(x) +#define bswap_32(x) OSSwapInt32(x) +#define bswap_64(x) OSSwapInt64(x) + +#elif defined(HAVE_BYTESWAP_H) +#include + +#elif defined(bswap32) +// FreeBSD defines bswap{16,32,64} in (already #included). +#define bswap_16(x) bswap16(x) +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) + +#elif defined(BSWAP_64) +// Solaris 10 defines BSWAP_{16,32,64} in (already #included). +#define bswap_16(x) BSWAP_16(x) +#define bswap_32(x) BSWAP_32(x) +#define bswap_64(x) BSWAP_64(x) + +#else + +inline uint16 bswap_16(uint16 x) { + return (x << 8) | (x >> 8); +} + +inline uint32 bswap_32(uint32 x) { + x = ((x & 0xff00ff00UL) >> 8) | ((x & 0x00ff00ffUL) << 8); + return (x >> 16) | (x << 16); +} + +inline uint64 bswap_64(uint64 x) { + x = ((x & 0xff00ff00ff00ff00ULL) >> 8) | ((x & 0x00ff00ff00ff00ffULL) << 8); + x = ((x & 0xffff0000ffff0000ULL) >> 16) | ((x & 0x0000ffff0000ffffULL) << 16); + return (x >> 32) | (x << 32); +} + +#endif + +#endif // defined(SNAPPY_IS_BIG_ENDIAN) + +// Convert to little-endian storage, opposite of network format. +// Convert x from host to little endian: x = LittleEndian.FromHost(x); +// convert x from little endian to host: x = LittleEndian.ToHost(x); +// +// Store values into unaligned memory converting to little endian order: +// LittleEndian.Store16(p, x); +// +// Load unaligned values stored in little endian converting to host order: +// x = LittleEndian.Load16(p); +class LittleEndian { + public: + // Conversion functions. +#if defined(SNAPPY_IS_BIG_ENDIAN) + + static uint16 FromHost16(uint16 x) { return bswap_16(x); } + static uint16 ToHost16(uint16 x) { return bswap_16(x); } + + static uint32 FromHost32(uint32 x) { return bswap_32(x); } + static uint32 ToHost32(uint32 x) { return bswap_32(x); } + + static bool IsLittleEndian() { return false; } + +#else // !defined(SNAPPY_IS_BIG_ENDIAN) + + static uint16 FromHost16(uint16 x) { return x; } + static uint16 ToHost16(uint16 x) { return x; } + + static uint32 FromHost32(uint32 x) { return x; } + static uint32 ToHost32(uint32 x) { return x; } + + static bool IsLittleEndian() { return true; } + +#endif // !defined(SNAPPY_IS_BIG_ENDIAN) + + // Functions to do unaligned loads and stores in little-endian order. + static uint16 Load16(const void *p) { + return ToHost16(UNALIGNED_LOAD16(p)); + } + + static void Store16(void *p, uint16 v) { + UNALIGNED_STORE16(p, FromHost16(v)); + } + + static uint32 Load32(const void *p) { + return ToHost32(UNALIGNED_LOAD32(p)); + } + + static void Store32(void *p, uint32 v) { + UNALIGNED_STORE32(p, FromHost32(v)); + } +}; + +// Some bit-manipulation functions. +class Bits { + public: + // Return floor(log2(n)) for positive integer n. Returns -1 iff n == 0. + static int Log2Floor(uint32 n); + + // Return the first set least / most significant bit, 0-indexed. Returns an + // undefined value if n == 0. FindLSBSetNonZero() is similar to ffs() except + // that it's 0-indexed. + static int FindLSBSetNonZero(uint32 n); + +#if defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) + static int FindLSBSetNonZero64(uint64 n); +#endif // defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) + + private: + // No copying + Bits(const Bits&); + void operator=(const Bits&); +}; + +#ifdef HAVE_BUILTIN_CTZ + +inline int Bits::Log2Floor(uint32 n) { + return n == 0 ? -1 : 31 ^ __builtin_clz(n); +} + +inline int Bits::FindLSBSetNonZero(uint32 n) { + return __builtin_ctz(n); +} + +#if defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) +inline int Bits::FindLSBSetNonZero64(uint64 n) { + return __builtin_ctzll(n); +} +#endif // defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) + +#elif defined(_MSC_VER) + +inline int Bits::Log2Floor(uint32 n) { + unsigned long where; + if (_BitScanReverse(&where, n)) { + return where; + } else { + return -1; + } +} + +inline int Bits::FindLSBSetNonZero(uint32 n) { + unsigned long where; + if (_BitScanForward(&where, n)) return static_cast(where); + return 32; +} + +#if defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) +inline int Bits::FindLSBSetNonZero64(uint64 n) { + unsigned long where; + if (_BitScanForward64(&where, n)) return static_cast(where); + return 64; +} +#endif // defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) + +#else // Portable versions. + +inline int Bits::Log2Floor(uint32 n) { + if (n == 0) + return -1; + int log = 0; + uint32 value = n; + for (int i = 4; i >= 0; --i) { + int shift = (1 << i); + uint32 x = value >> shift; + if (x != 0) { + value = x; + log += shift; + } + } + assert(value == 1); + return log; +} + +inline int Bits::FindLSBSetNonZero(uint32 n) { + int rc = 31; + for (int i = 4, shift = 1 << 4; i >= 0; --i) { + const uint32 x = n << shift; + if (x != 0) { + n = x; + rc -= shift; + } + shift >>= 1; + } + return rc; +} + +#if defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) +// FindLSBSetNonZero64() is defined in terms of FindLSBSetNonZero(). +inline int Bits::FindLSBSetNonZero64(uint64 n) { + const uint32 bottombits = static_cast(n); + if (bottombits == 0) { + // Bottom bits are zero, so scan in top bits + return 32 + FindLSBSetNonZero(static_cast(n >> 32)); + } else { + return FindLSBSetNonZero(bottombits); + } +} +#endif // defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM) + +#endif // End portable versions. + +// Variable-length integer encoding. +class Varint { + public: + // Maximum lengths of varint encoding of uint32. + static const int kMax32 = 5; + + // Attempts to parse a varint32 from a prefix of the bytes in [ptr,limit-1]. + // Never reads a character at or beyond limit. If a valid/terminated varint32 + // was found in the range, stores it in *OUTPUT and returns a pointer just + // past the last byte of the varint32. Else returns NULL. On success, + // "result <= limit". + static const char* Parse32WithLimit(const char* ptr, const char* limit, + uint32* OUTPUT); + + // REQUIRES "ptr" points to a buffer of length sufficient to hold "v". + // EFFECTS Encodes "v" into "ptr" and returns a pointer to the + // byte just past the last encoded byte. + static char* Encode32(char* ptr, uint32 v); + + // EFFECTS Appends the varint representation of "value" to "*s". + static void Append32(string* s, uint32 value); +}; + +inline const char* Varint::Parse32WithLimit(const char* p, + const char* l, + uint32* OUTPUT) { + const unsigned char* ptr = reinterpret_cast(p); + const unsigned char* limit = reinterpret_cast(l); + uint32 b, result; + if (ptr >= limit) return NULL; + b = *(ptr++); result = b & 127; if (b < 128) goto done; + if (ptr >= limit) return NULL; + b = *(ptr++); result |= (b & 127) << 7; if (b < 128) goto done; + if (ptr >= limit) return NULL; + b = *(ptr++); result |= (b & 127) << 14; if (b < 128) goto done; + if (ptr >= limit) return NULL; + b = *(ptr++); result |= (b & 127) << 21; if (b < 128) goto done; + if (ptr >= limit) return NULL; + b = *(ptr++); result |= (b & 127) << 28; if (b < 16) goto done; + return NULL; // Value is too long to be a varint32 + done: + *OUTPUT = result; + return reinterpret_cast(ptr); +} + +inline char* Varint::Encode32(char* sptr, uint32 v) { + // Operate on characters as unsigneds + unsigned char* ptr = reinterpret_cast(sptr); + static const int B = 128; + if (v < (1<<7)) { + *(ptr++) = v; + } else if (v < (1<<14)) { + *(ptr++) = v | B; + *(ptr++) = v>>7; + } else if (v < (1<<21)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = v>>14; + } else if (v < (1<<28)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = v>>21; + } else { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = (v>>21) | B; + *(ptr++) = v>>28; + } + return reinterpret_cast(ptr); +} + +// If you know the internal layout of the std::string in use, you can +// replace this function with one that resizes the string without +// filling the new space with zeros (if applicable) -- +// it will be non-portable but faster. +inline void STLStringResizeUninitialized(string* s, size_t new_size) { + s->resize(new_size); +} + +// Return a mutable char* pointing to a string's internal buffer, +// which may not be null-terminated. Writing through this pointer will +// modify the string. +// +// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the +// next call to a string method that invalidates iterators. +// +// As of 2006-04, there is no standard-blessed way of getting a +// mutable reference to a string's internal buffer. However, issue 530 +// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-defects.html#530) +// proposes this as the method. It will officially be part of the standard +// for C++0x. This should already work on all current implementations. +inline char* string_as_array(string* str) { + return str->empty() ? NULL : &*str->begin(); +} + +} // namespace snappy + +#endif // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-stubs-public.h.in b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-stubs-public.h.in new file mode 100644 index 0000000..3fd79bb --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-stubs-public.h.in @@ -0,0 +1,94 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: sesse@google.com (Steinar H. Gunderson) +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Various type stubs for the open-source version of Snappy. +// +// This file cannot include config.h, as it is included from snappy.h, +// which is a public header. Instead, snappy-stubs-public.h is generated by +// from snappy-stubs-public.h.in at configure time. + +#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ +#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ + +#if ${HAVE_STDINT_H_01} // HAVE_STDINT_H +#include +#endif // HAVE_STDDEF_H + +#if ${HAVE_STDDEF_H_01} // HAVE_STDDEF_H +#include +#endif // HAVE_STDDEF_H + +#if ${HAVE_SYS_UIO_H_01} // HAVE_SYS_UIO_H +#include +#endif // HAVE_SYS_UIO_H + +#define SNAPPY_MAJOR ${SNAPPY_MAJOR} +#define SNAPPY_MINOR ${SNAPPY_MINOR} +#define SNAPPY_PATCHLEVEL ${SNAPPY_PATCHLEVEL} +#define SNAPPY_VERSION \ + ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL) + +#include + +namespace snappy { + +#if ${HAVE_STDINT_H_01} // HAVE_STDINT_H +typedef int8_t int8; +typedef uint8_t uint8; +typedef int16_t int16; +typedef uint16_t uint16; +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#else +typedef signed char int8; +typedef unsigned char uint8; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +typedef long long int64; +typedef unsigned long long uint64; +#endif // HAVE_STDINT_H + +typedef std::string string; + +#if !${HAVE_SYS_UIO_H_01} // !HAVE_SYS_UIO_H +// Windows does not have an iovec type, yet the concept is universally useful. +// It is simple to define it ourselves, so we put it inside our own namespace. +struct iovec { + void* iov_base; + size_t iov_len; +}; +#endif // !HAVE_SYS_UIO_H + +} // namespace snappy + +#endif // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-test.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-test.cc new file mode 100644 index 0000000..01d5541 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-test.cc @@ -0,0 +1,612 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Various stubs for the unit tests for the open-source version of Snappy. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_WINDOWS_H +// Needed to be able to use std::max without workarounds in the source code. +// https://support.microsoft.com/en-us/help/143208/prb-using-stl-in-windows-program-can-cause-min-max-conflicts +#define NOMINMAX +#include +#endif + +#include "snappy-test.h" + +#include + +DEFINE_bool(run_microbenchmarks, true, + "Run microbenchmarks before doing anything else."); + +namespace snappy { + +string ReadTestDataFile(const string& base, size_t size_limit) { + string contents; + const char* srcdir = getenv("srcdir"); // This is set by Automake. + string prefix; + if (srcdir) { + prefix = string(srcdir) + "/"; + } + file::GetContents(prefix + "testdata/" + base, &contents, file::Defaults() + ).CheckSuccess(); + if (size_limit > 0) { + contents = contents.substr(0, size_limit); + } + return contents; +} + +string ReadTestDataFile(const string& base) { + return ReadTestDataFile(base, 0); +} + +string StringPrintf(const char* format, ...) { + char buf[4096]; + va_list ap; + va_start(ap, format); + vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); + return buf; +} + +bool benchmark_running = false; +int64 benchmark_real_time_us = 0; +int64 benchmark_cpu_time_us = 0; +string *benchmark_label = NULL; +int64 benchmark_bytes_processed = 0; + +void ResetBenchmarkTiming() { + benchmark_real_time_us = 0; + benchmark_cpu_time_us = 0; +} + +#ifdef WIN32 +LARGE_INTEGER benchmark_start_real; +FILETIME benchmark_start_cpu; +#else // WIN32 +struct timeval benchmark_start_real; +struct rusage benchmark_start_cpu; +#endif // WIN32 + +void StartBenchmarkTiming() { +#ifdef WIN32 + QueryPerformanceCounter(&benchmark_start_real); + FILETIME dummy; + CHECK(GetProcessTimes( + GetCurrentProcess(), &dummy, &dummy, &dummy, &benchmark_start_cpu)); +#else + gettimeofday(&benchmark_start_real, NULL); + if (getrusage(RUSAGE_SELF, &benchmark_start_cpu) == -1) { + perror("getrusage(RUSAGE_SELF)"); + exit(1); + } +#endif + benchmark_running = true; +} + +void StopBenchmarkTiming() { + if (!benchmark_running) { + return; + } + +#ifdef WIN32 + LARGE_INTEGER benchmark_stop_real; + LARGE_INTEGER benchmark_frequency; + QueryPerformanceCounter(&benchmark_stop_real); + QueryPerformanceFrequency(&benchmark_frequency); + + double elapsed_real = static_cast( + benchmark_stop_real.QuadPart - benchmark_start_real.QuadPart) / + benchmark_frequency.QuadPart; + benchmark_real_time_us += elapsed_real * 1e6 + 0.5; + + FILETIME benchmark_stop_cpu, dummy; + CHECK(GetProcessTimes( + GetCurrentProcess(), &dummy, &dummy, &dummy, &benchmark_stop_cpu)); + + ULARGE_INTEGER start_ulargeint; + start_ulargeint.LowPart = benchmark_start_cpu.dwLowDateTime; + start_ulargeint.HighPart = benchmark_start_cpu.dwHighDateTime; + + ULARGE_INTEGER stop_ulargeint; + stop_ulargeint.LowPart = benchmark_stop_cpu.dwLowDateTime; + stop_ulargeint.HighPart = benchmark_stop_cpu.dwHighDateTime; + + benchmark_cpu_time_us += + (stop_ulargeint.QuadPart - start_ulargeint.QuadPart + 5) / 10; +#else // WIN32 + struct timeval benchmark_stop_real; + gettimeofday(&benchmark_stop_real, NULL); + benchmark_real_time_us += + 1000000 * (benchmark_stop_real.tv_sec - benchmark_start_real.tv_sec); + benchmark_real_time_us += + (benchmark_stop_real.tv_usec - benchmark_start_real.tv_usec); + + struct rusage benchmark_stop_cpu; + if (getrusage(RUSAGE_SELF, &benchmark_stop_cpu) == -1) { + perror("getrusage(RUSAGE_SELF)"); + exit(1); + } + benchmark_cpu_time_us += 1000000 * (benchmark_stop_cpu.ru_utime.tv_sec - + benchmark_start_cpu.ru_utime.tv_sec); + benchmark_cpu_time_us += (benchmark_stop_cpu.ru_utime.tv_usec - + benchmark_start_cpu.ru_utime.tv_usec); +#endif // WIN32 + + benchmark_running = false; +} + +void SetBenchmarkLabel(const string& str) { + if (benchmark_label) { + delete benchmark_label; + } + benchmark_label = new string(str); +} + +void SetBenchmarkBytesProcessed(int64 bytes) { + benchmark_bytes_processed = bytes; +} + +struct BenchmarkRun { + int64 real_time_us; + int64 cpu_time_us; +}; + +struct BenchmarkCompareCPUTime { + bool operator() (const BenchmarkRun& a, const BenchmarkRun& b) const { + return a.cpu_time_us < b.cpu_time_us; + } +}; + +void Benchmark::Run() { + for (int test_case_num = start_; test_case_num <= stop_; ++test_case_num) { + // Run a few iterations first to find out approximately how fast + // the benchmark is. + const int kCalibrateIterations = 100; + ResetBenchmarkTiming(); + StartBenchmarkTiming(); + (*function_)(kCalibrateIterations, test_case_num); + StopBenchmarkTiming(); + + // Let each test case run for about 200ms, but at least as many + // as we used to calibrate. + // Run five times and pick the median. + const int kNumRuns = 5; + const int kMedianPos = kNumRuns / 2; + int num_iterations = 0; + if (benchmark_real_time_us > 0) { + num_iterations = 200000 * kCalibrateIterations / benchmark_real_time_us; + } + num_iterations = std::max(num_iterations, kCalibrateIterations); + BenchmarkRun benchmark_runs[kNumRuns]; + + for (int run = 0; run < kNumRuns; ++run) { + ResetBenchmarkTiming(); + StartBenchmarkTiming(); + (*function_)(num_iterations, test_case_num); + StopBenchmarkTiming(); + + benchmark_runs[run].real_time_us = benchmark_real_time_us; + benchmark_runs[run].cpu_time_us = benchmark_cpu_time_us; + } + + string heading = StringPrintf("%s/%d", name_.c_str(), test_case_num); + string human_readable_speed; + + std::nth_element(benchmark_runs, + benchmark_runs + kMedianPos, + benchmark_runs + kNumRuns, + BenchmarkCompareCPUTime()); + int64 real_time_us = benchmark_runs[kMedianPos].real_time_us; + int64 cpu_time_us = benchmark_runs[kMedianPos].cpu_time_us; + if (cpu_time_us <= 0) { + human_readable_speed = "?"; + } else { + int64 bytes_per_second = + benchmark_bytes_processed * 1000000 / cpu_time_us; + if (bytes_per_second < 1024) { + human_readable_speed = StringPrintf("%dB/s", bytes_per_second); + } else if (bytes_per_second < 1024 * 1024) { + human_readable_speed = StringPrintf( + "%.1fkB/s", bytes_per_second / 1024.0f); + } else if (bytes_per_second < 1024 * 1024 * 1024) { + human_readable_speed = StringPrintf( + "%.1fMB/s", bytes_per_second / (1024.0f * 1024.0f)); + } else { + human_readable_speed = StringPrintf( + "%.1fGB/s", bytes_per_second / (1024.0f * 1024.0f * 1024.0f)); + } + } + + fprintf(stderr, +#ifdef WIN32 + "%-18s %10I64d %10I64d %10d %s %s\n", +#else + "%-18s %10lld %10lld %10d %s %s\n", +#endif + heading.c_str(), + static_cast(real_time_us * 1000 / num_iterations), + static_cast(cpu_time_us * 1000 / num_iterations), + num_iterations, + human_readable_speed.c_str(), + benchmark_label->c_str()); + } +} + +#ifdef HAVE_LIBZ + +ZLib::ZLib() + : comp_init_(false), + uncomp_init_(false) { + Reinit(); +} + +ZLib::~ZLib() { + if (comp_init_) { deflateEnd(&comp_stream_); } + if (uncomp_init_) { inflateEnd(&uncomp_stream_); } +} + +void ZLib::Reinit() { + compression_level_ = Z_DEFAULT_COMPRESSION; + window_bits_ = MAX_WBITS; + mem_level_ = 8; // DEF_MEM_LEVEL + if (comp_init_) { + deflateEnd(&comp_stream_); + comp_init_ = false; + } + if (uncomp_init_) { + inflateEnd(&uncomp_stream_); + uncomp_init_ = false; + } + first_chunk_ = true; +} + +void ZLib::Reset() { + first_chunk_ = true; +} + +// --------- COMPRESS MODE + +// Initialization method to be called if we hit an error while +// compressing. On hitting an error, call this method before returning +// the error. +void ZLib::CompressErrorInit() { + deflateEnd(&comp_stream_); + comp_init_ = false; + Reset(); +} + +int ZLib::DeflateInit() { + return deflateInit2(&comp_stream_, + compression_level_, + Z_DEFLATED, + window_bits_, + mem_level_, + Z_DEFAULT_STRATEGY); +} + +int ZLib::CompressInit(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen) { + int err; + + comp_stream_.next_in = (Bytef*)source; + comp_stream_.avail_in = (uInt)*sourceLen; + if ((uLong)comp_stream_.avail_in != *sourceLen) return Z_BUF_ERROR; + comp_stream_.next_out = dest; + comp_stream_.avail_out = (uInt)*destLen; + if ((uLong)comp_stream_.avail_out != *destLen) return Z_BUF_ERROR; + + if ( !first_chunk_ ) // only need to set up stream the first time through + return Z_OK; + + if (comp_init_) { // we've already initted it + err = deflateReset(&comp_stream_); + if (err != Z_OK) { + LOG(WARNING) << "ERROR: Can't reset compress object; creating a new one"; + deflateEnd(&comp_stream_); + comp_init_ = false; + } + } + if (!comp_init_) { // first use + comp_stream_.zalloc = (alloc_func)0; + comp_stream_.zfree = (free_func)0; + comp_stream_.opaque = (voidpf)0; + err = DeflateInit(); + if (err != Z_OK) return err; + comp_init_ = true; + } + return Z_OK; +} + +// In a perfect world we'd always have the full buffer to compress +// when the time came, and we could just call Compress(). Alas, we +// want to do chunked compression on our webserver. In this +// application, we compress the header, send it off, then compress the +// results, send them off, then compress the footer. Thus we need to +// use the chunked compression features of zlib. +int ZLib::CompressAtMostOrAll(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen, + int flush_mode) { // Z_FULL_FLUSH or Z_FINISH + int err; + + if ( (err=CompressInit(dest, destLen, source, sourceLen)) != Z_OK ) + return err; + + // This is used to figure out how many bytes we wrote *this chunk* + int compressed_size = comp_stream_.total_out; + + // Some setup happens only for the first chunk we compress in a run + if ( first_chunk_ ) { + first_chunk_ = false; + } + + // flush_mode is Z_FINISH for all mode, Z_SYNC_FLUSH for incremental + // compression. + err = deflate(&comp_stream_, flush_mode); + + *sourceLen = comp_stream_.avail_in; + + if ((err == Z_STREAM_END || err == Z_OK) + && comp_stream_.avail_in == 0 + && comp_stream_.avail_out != 0 ) { + // we processed everything ok and the output buffer was large enough. + ; + } else if (err == Z_STREAM_END && comp_stream_.avail_in > 0) { + return Z_BUF_ERROR; // should never happen + } else if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR) { + // an error happened + CompressErrorInit(); + return err; + } else if (comp_stream_.avail_out == 0) { // not enough space + err = Z_BUF_ERROR; + } + + assert(err == Z_OK || err == Z_STREAM_END || err == Z_BUF_ERROR); + if (err == Z_STREAM_END) + err = Z_OK; + + // update the crc and other metadata + compressed_size = comp_stream_.total_out - compressed_size; // delta + *destLen = compressed_size; + + return err; +} + +int ZLib::CompressChunkOrAll(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int flush_mode) { // Z_FULL_FLUSH or Z_FINISH + const int ret = + CompressAtMostOrAll(dest, destLen, source, &sourceLen, flush_mode); + if (ret == Z_BUF_ERROR) + CompressErrorInit(); + return ret; +} + +// This routine only initializes the compression stream once. Thereafter, it +// just does a deflateReset on the stream, which should be faster. +int ZLib::Compress(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen) { + int err; + if ( (err=CompressChunkOrAll(dest, destLen, source, sourceLen, + Z_FINISH)) != Z_OK ) + return err; + Reset(); // reset for next call to Compress + + return Z_OK; +} + + +// --------- UNCOMPRESS MODE + +int ZLib::InflateInit() { + return inflateInit2(&uncomp_stream_, MAX_WBITS); +} + +// Initialization method to be called if we hit an error while +// uncompressing. On hitting an error, call this method before +// returning the error. +void ZLib::UncompressErrorInit() { + inflateEnd(&uncomp_stream_); + uncomp_init_ = false; + Reset(); +} + +int ZLib::UncompressInit(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen) { + int err; + + uncomp_stream_.next_in = (Bytef*)source; + uncomp_stream_.avail_in = (uInt)*sourceLen; + // Check for source > 64K on 16-bit machine: + if ((uLong)uncomp_stream_.avail_in != *sourceLen) return Z_BUF_ERROR; + + uncomp_stream_.next_out = dest; + uncomp_stream_.avail_out = (uInt)*destLen; + if ((uLong)uncomp_stream_.avail_out != *destLen) return Z_BUF_ERROR; + + if ( !first_chunk_ ) // only need to set up stream the first time through + return Z_OK; + + if (uncomp_init_) { // we've already initted it + err = inflateReset(&uncomp_stream_); + if (err != Z_OK) { + LOG(WARNING) + << "ERROR: Can't reset uncompress object; creating a new one"; + UncompressErrorInit(); + } + } + if (!uncomp_init_) { + uncomp_stream_.zalloc = (alloc_func)0; + uncomp_stream_.zfree = (free_func)0; + uncomp_stream_.opaque = (voidpf)0; + err = InflateInit(); + if (err != Z_OK) return err; + uncomp_init_ = true; + } + return Z_OK; +} + +// If you compressed your data a chunk at a time, with CompressChunk, +// you can uncompress it a chunk at a time with UncompressChunk. +// Only difference bewteen chunked and unchunked uncompression +// is the flush mode we use: Z_SYNC_FLUSH (chunked) or Z_FINISH (unchunked). +int ZLib::UncompressAtMostOrAll(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen, + int flush_mode) { // Z_SYNC_FLUSH or Z_FINISH + int err = Z_OK; + + if ( (err=UncompressInit(dest, destLen, source, sourceLen)) != Z_OK ) { + LOG(WARNING) << "UncompressInit: Error: " << err << " SourceLen: " + << *sourceLen; + return err; + } + + // This is used to figure out how many output bytes we wrote *this chunk*: + const uLong old_total_out = uncomp_stream_.total_out; + + // This is used to figure out how many input bytes we read *this chunk*: + const uLong old_total_in = uncomp_stream_.total_in; + + // Some setup happens only for the first chunk we compress in a run + if ( first_chunk_ ) { + first_chunk_ = false; // so we don't do this again + + // For the first chunk *only* (to avoid infinite troubles), we let + // there be no actual data to uncompress. This sometimes triggers + // when the input is only the gzip header, say. + if ( *sourceLen == 0 ) { + *destLen = 0; + return Z_OK; + } + } + + // We'll uncompress as much as we can. If we end OK great, otherwise + // if we get an error that seems to be the gzip footer, we store the + // gzip footer and return OK, otherwise we return the error. + + // flush_mode is Z_SYNC_FLUSH for chunked mode, Z_FINISH for all mode. + err = inflate(&uncomp_stream_, flush_mode); + + // Figure out how many bytes of the input zlib slurped up: + const uLong bytes_read = uncomp_stream_.total_in - old_total_in; + CHECK_LE(source + bytes_read, source + *sourceLen); + *sourceLen = uncomp_stream_.avail_in; + + if ((err == Z_STREAM_END || err == Z_OK) // everything went ok + && uncomp_stream_.avail_in == 0) { // and we read it all + ; + } else if (err == Z_STREAM_END && uncomp_stream_.avail_in > 0) { + LOG(WARNING) + << "UncompressChunkOrAll: Received some extra data, bytes total: " + << uncomp_stream_.avail_in << " bytes: " + << std::string(reinterpret_cast(uncomp_stream_.next_in), + std::min(int(uncomp_stream_.avail_in), 20)); + UncompressErrorInit(); + return Z_DATA_ERROR; // what's the extra data for? + } else if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR) { + // an error happened + LOG(WARNING) << "UncompressChunkOrAll: Error: " << err + << " avail_out: " << uncomp_stream_.avail_out; + UncompressErrorInit(); + return err; + } else if (uncomp_stream_.avail_out == 0) { + err = Z_BUF_ERROR; + } + + assert(err == Z_OK || err == Z_BUF_ERROR || err == Z_STREAM_END); + if (err == Z_STREAM_END) + err = Z_OK; + + *destLen = uncomp_stream_.total_out - old_total_out; // size for this call + + return err; +} + +int ZLib::UncompressChunkOrAll(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int flush_mode) { // Z_SYNC_FLUSH or Z_FINISH + const int ret = + UncompressAtMostOrAll(dest, destLen, source, &sourceLen, flush_mode); + if (ret == Z_BUF_ERROR) + UncompressErrorInit(); + return ret; +} + +int ZLib::UncompressAtMost(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen) { + return UncompressAtMostOrAll(dest, destLen, source, sourceLen, Z_SYNC_FLUSH); +} + +// We make sure we've uncompressed everything, that is, the current +// uncompress stream is at a compressed-buffer-EOF boundary. In gzip +// mode, we also check the gzip footer to make sure we pass the gzip +// consistency checks. We RETURN true iff both types of checks pass. +bool ZLib::UncompressChunkDone() { + assert(!first_chunk_ && uncomp_init_); + // Make sure we're at the end-of-compressed-data point. This means + // if we call inflate with Z_FINISH we won't consume any input or + // write any output + Bytef dummyin, dummyout; + uLongf dummylen = 0; + if ( UncompressChunkOrAll(&dummyout, &dummylen, &dummyin, 0, Z_FINISH) + != Z_OK ) { + return false; + } + + // Make sure that when we exit, we can start a new round of chunks later + Reset(); + + return true; +} + +// Uncompresses the source buffer into the destination buffer. +// The destination buffer must be long enough to hold the entire +// decompressed contents. +// +// We only initialize the uncomp_stream once. Thereafter, we use +// inflateReset, which should be faster. +// +// Returns Z_OK on success, otherwise, it returns a zlib error code. +int ZLib::Uncompress(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen) { + int err; + if ( (err=UncompressChunkOrAll(dest, destLen, source, sourceLen, + Z_FINISH)) != Z_OK ) { + Reset(); // let us try to compress again + return err; + } + if ( !UncompressChunkDone() ) // calls Reset() + return Z_DATA_ERROR; + return Z_OK; // stream_end is ok +} + +#endif // HAVE_LIBZ + +} // namespace snappy diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-test.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-test.h new file mode 100644 index 0000000..078f321 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy-test.h @@ -0,0 +1,573 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Various stubs for the unit tests for the open-source version of Snappy. + +#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_TEST_H_ +#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_TEST_H_ + +#include +#include + +#include "snappy-stubs-internal.h" + +#include +#include + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include + +#ifdef HAVE_GTEST + +#include +#undef TYPED_TEST +#define TYPED_TEST TEST +#define INIT_GTEST(argc, argv) ::testing::InitGoogleTest(argc, *argv) + +#else + +// Stubs for if the user doesn't have Google Test installed. + +#define TEST(test_case, test_subcase) \ + void Test_ ## test_case ## _ ## test_subcase() +#define INIT_GTEST(argc, argv) + +#define TYPED_TEST TEST +#define EXPECT_EQ CHECK_EQ +#define EXPECT_NE CHECK_NE +#define EXPECT_FALSE(cond) CHECK(!(cond)) + +#endif + +#ifdef HAVE_GFLAGS + +#include + +// This is tricky; both gflags and Google Test want to look at the command line +// arguments. Google Test seems to be the most happy with unknown arguments, +// though, so we call it first and hope for the best. +#define InitGoogle(argv0, argc, argv, remove_flags) \ + INIT_GTEST(argc, argv); \ + google::ParseCommandLineFlags(argc, argv, remove_flags); + +#else + +// If we don't have the gflags package installed, these can only be +// changed at compile time. +#define DEFINE_int32(flag_name, default_value, description) \ + static int FLAGS_ ## flag_name = default_value; + +#define InitGoogle(argv0, argc, argv, remove_flags) \ + INIT_GTEST(argc, argv) + +#endif + +#ifdef HAVE_LIBZ +#include "zlib.h" +#endif + +#ifdef HAVE_LIBLZO2 +#include "lzo/lzo1x.h" +#endif + +namespace { + +namespace file { + int Defaults() { return 0; } + + class DummyStatus { + public: + void CheckSuccess() { } + }; + + DummyStatus GetContents( + const std::string& filename, std::string* data, int unused) { + FILE* fp = fopen(filename.c_str(), "rb"); + if (fp == NULL) { + perror(filename.c_str()); + exit(1); + } + + data->clear(); + while (!feof(fp)) { + char buf[4096]; + size_t ret = fread(buf, 1, 4096, fp); + if (ret == 0 && ferror(fp)) { + perror("fread"); + exit(1); + } + data->append(std::string(buf, ret)); + } + + fclose(fp); + + return DummyStatus(); + } + + inline DummyStatus SetContents( + const std::string& filename, const std::string& str, int unused) { + FILE* fp = fopen(filename.c_str(), "wb"); + if (fp == NULL) { + perror(filename.c_str()); + exit(1); + } + + int ret = fwrite(str.data(), str.size(), 1, fp); + if (ret != 1) { + perror("fwrite"); + exit(1); + } + + fclose(fp); + + return DummyStatus(); + } +} // namespace file + +} // namespace + +namespace snappy { + +#define FLAGS_test_random_seed 301 +typedef string TypeParam; + +void Test_CorruptedTest_VerifyCorrupted(); +void Test_Snappy_SimpleTests(); +void Test_Snappy_MaxBlowup(); +void Test_Snappy_RandomData(); +void Test_Snappy_FourByteOffset(); +void Test_SnappyCorruption_TruncatedVarint(); +void Test_SnappyCorruption_UnterminatedVarint(); +void Test_SnappyCorruption_OverflowingVarint(); +void Test_Snappy_ReadPastEndOfBuffer(); +void Test_Snappy_FindMatchLength(); +void Test_Snappy_FindMatchLengthRandom(); + +string ReadTestDataFile(const string& base, size_t size_limit); + +string ReadTestDataFile(const string& base); + +// A sprintf() variant that returns a std::string. +// Not safe for general use due to truncation issues. +string StringPrintf(const char* format, ...); + +// A simple, non-cryptographically-secure random generator. +class ACMRandom { + public: + explicit ACMRandom(uint32 seed) : seed_(seed) {} + + int32 Next(); + + int32 Uniform(int32 n) { + return Next() % n; + } + uint8 Rand8() { + return static_cast((Next() >> 1) & 0x000000ff); + } + bool OneIn(int X) { return Uniform(X) == 0; } + + // Skewed: pick "base" uniformly from range [0,max_log] and then + // return "base" random bits. The effect is to pick a number in the + // range [0,2^max_log-1] with bias towards smaller numbers. + int32 Skewed(int max_log); + + private: + static const uint32 M = 2147483647L; // 2^31-1 + uint32 seed_; +}; + +inline int32 ACMRandom::Next() { + static const uint64 A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 + // We are computing + // seed_ = (seed_ * A) % M, where M = 2^31-1 + // + // seed_ must not be zero or M, or else all subsequent computed values + // will be zero or M respectively. For all other values, seed_ will end + // up cycling through every number in [1,M-1] + uint64 product = seed_ * A; + + // Compute (product % M) using the fact that ((x << 31) % M) == x. + seed_ = (product >> 31) + (product & M); + // The first reduction may overflow by 1 bit, so we may need to repeat. + // mod == M is not possible; using > allows the faster sign-bit-based test. + if (seed_ > M) { + seed_ -= M; + } + return seed_; +} + +inline int32 ACMRandom::Skewed(int max_log) { + const int32 base = (Next() - 1) % (max_log+1); + return (Next() - 1) & ((1u << base)-1); +} + +// A wall-time clock. This stub is not super-accurate, nor resistant to the +// system time changing. +class CycleTimer { + public: + CycleTimer() : real_time_us_(0) {} + + void Start() { +#ifdef WIN32 + QueryPerformanceCounter(&start_); +#else + gettimeofday(&start_, NULL); +#endif + } + + void Stop() { +#ifdef WIN32 + LARGE_INTEGER stop; + LARGE_INTEGER frequency; + QueryPerformanceCounter(&stop); + QueryPerformanceFrequency(&frequency); + + double elapsed = static_cast(stop.QuadPart - start_.QuadPart) / + frequency.QuadPart; + real_time_us_ += elapsed * 1e6 + 0.5; +#else + struct timeval stop; + gettimeofday(&stop, NULL); + + real_time_us_ += 1000000 * (stop.tv_sec - start_.tv_sec); + real_time_us_ += (stop.tv_usec - start_.tv_usec); +#endif + } + + double Get() { + return real_time_us_ * 1e-6; + } + + private: + int64 real_time_us_; +#ifdef WIN32 + LARGE_INTEGER start_; +#else + struct timeval start_; +#endif +}; + +// Minimalistic microbenchmark framework. + +typedef void (*BenchmarkFunction)(int, int); + +class Benchmark { + public: + Benchmark(const string& name, BenchmarkFunction function) : + name_(name), function_(function) {} + + Benchmark* DenseRange(int start, int stop) { + start_ = start; + stop_ = stop; + return this; + } + + void Run(); + + private: + const string name_; + const BenchmarkFunction function_; + int start_, stop_; +}; +#define BENCHMARK(benchmark_name) \ + Benchmark* Benchmark_ ## benchmark_name = \ + (new Benchmark(#benchmark_name, benchmark_name)) + +extern Benchmark* Benchmark_BM_UFlat; +extern Benchmark* Benchmark_BM_UIOVec; +extern Benchmark* Benchmark_BM_UValidate; +extern Benchmark* Benchmark_BM_ZFlat; + +void ResetBenchmarkTiming(); +void StartBenchmarkTiming(); +void StopBenchmarkTiming(); +void SetBenchmarkLabel(const string& str); +void SetBenchmarkBytesProcessed(int64 bytes); + +#ifdef HAVE_LIBZ + +// Object-oriented wrapper around zlib. +class ZLib { + public: + ZLib(); + ~ZLib(); + + // Wipe a ZLib object to a virgin state. This differs from Reset() + // in that it also breaks any state. + void Reinit(); + + // Call this to make a zlib buffer as good as new. Here's the only + // case where they differ: + // CompressChunk(a); CompressChunk(b); CompressChunkDone(); vs + // CompressChunk(a); Reset(); CompressChunk(b); CompressChunkDone(); + // You'll want to use Reset(), then, when you interrupt a compress + // (or uncompress) in the middle of a chunk and want to start over. + void Reset(); + + // According to the zlib manual, when you Compress, the destination + // buffer must have size at least src + .1%*src + 12. This function + // helps you calculate that. Augment this to account for a potential + // gzip header and footer, plus a few bytes of slack. + static int MinCompressbufSize(int uncompress_size) { + return uncompress_size + uncompress_size/1000 + 40; + } + + // Compresses the source buffer into the destination buffer. + // sourceLen is the byte length of the source buffer. + // Upon entry, destLen is the total size of the destination buffer, + // which must be of size at least MinCompressbufSize(sourceLen). + // Upon exit, destLen is the actual size of the compressed buffer. + // + // This function can be used to compress a whole file at once if the + // input file is mmap'ed. + // + // Returns Z_OK if success, Z_MEM_ERROR if there was not + // enough memory, Z_BUF_ERROR if there was not enough room in the + // output buffer. Note that if the output buffer is exactly the same + // size as the compressed result, we still return Z_BUF_ERROR. + // (check CL#1936076) + int Compress(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen); + + // Uncompresses the source buffer into the destination buffer. + // The destination buffer must be long enough to hold the entire + // decompressed contents. + // + // Returns Z_OK on success, otherwise, it returns a zlib error code. + int Uncompress(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen); + + // Uncompress data one chunk at a time -- ie you can call this + // more than once. To get this to work you need to call per-chunk + // and "done" routines. + // + // Returns Z_OK if success, Z_MEM_ERROR if there was not + // enough memory, Z_BUF_ERROR if there was not enough room in the + // output buffer. + + int UncompressAtMost(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen); + + // Checks gzip footer information, as needed. Mostly this just + // makes sure the checksums match. Whenever you call this, it + // will assume the last 8 bytes from the previous UncompressChunk + // call are the footer. Returns true iff everything looks ok. + bool UncompressChunkDone(); + + private: + int InflateInit(); // sets up the zlib inflate structure + int DeflateInit(); // sets up the zlib deflate structure + + // These init the zlib data structures for compressing/uncompressing + int CompressInit(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen); + int UncompressInit(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen); + // Initialization method to be called if we hit an error while + // uncompressing. On hitting an error, call this method before + // returning the error. + void UncompressErrorInit(); + + // Helper function for Compress + int CompressChunkOrAll(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int flush_mode); + int CompressAtMostOrAll(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen, + int flush_mode); + + // Likewise for UncompressAndUncompressChunk + int UncompressChunkOrAll(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int flush_mode); + + int UncompressAtMostOrAll(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen, + int flush_mode); + + // Initialization method to be called if we hit an error while + // compressing. On hitting an error, call this method before + // returning the error. + void CompressErrorInit(); + + int compression_level_; // compression level + int window_bits_; // log base 2 of the window size used in compression + int mem_level_; // specifies the amount of memory to be used by + // compressor (1-9) + z_stream comp_stream_; // Zlib stream data structure + bool comp_init_; // True if we have initialized comp_stream_ + z_stream uncomp_stream_; // Zlib stream data structure + bool uncomp_init_; // True if we have initialized uncomp_stream_ + + // These are used only with chunked compression. + bool first_chunk_; // true if we need to emit headers with this chunk +}; + +#endif // HAVE_LIBZ + +} // namespace snappy + +DECLARE_bool(run_microbenchmarks); + +static inline void RunSpecifiedBenchmarks() { + if (!FLAGS_run_microbenchmarks) { + return; + } + + fprintf(stderr, "Running microbenchmarks.\n"); +#ifndef NDEBUG + fprintf(stderr, "WARNING: Compiled with assertions enabled, will be slow.\n"); +#endif +#ifndef __OPTIMIZE__ + fprintf(stderr, "WARNING: Compiled without optimization, will be slow.\n"); +#endif + fprintf(stderr, "Benchmark Time(ns) CPU(ns) Iterations\n"); + fprintf(stderr, "---------------------------------------------------\n"); + + snappy::Benchmark_BM_UFlat->Run(); + snappy::Benchmark_BM_UIOVec->Run(); + snappy::Benchmark_BM_UValidate->Run(); + snappy::Benchmark_BM_ZFlat->Run(); + + fprintf(stderr, "\n"); +} + +#ifndef HAVE_GTEST + +static inline int RUN_ALL_TESTS() { + fprintf(stderr, "Running correctness tests.\n"); + snappy::Test_CorruptedTest_VerifyCorrupted(); + snappy::Test_Snappy_SimpleTests(); + snappy::Test_Snappy_MaxBlowup(); + snappy::Test_Snappy_RandomData(); + snappy::Test_Snappy_FourByteOffset(); + snappy::Test_SnappyCorruption_TruncatedVarint(); + snappy::Test_SnappyCorruption_UnterminatedVarint(); + snappy::Test_SnappyCorruption_OverflowingVarint(); + snappy::Test_Snappy_ReadPastEndOfBuffer(); + snappy::Test_Snappy_FindMatchLength(); + snappy::Test_Snappy_FindMatchLengthRandom(); + fprintf(stderr, "All tests passed.\n"); + + return 0; +} + +#endif // HAVE_GTEST + +// For main(). +namespace snappy { + +// Logging. + +#define LOG(level) LogMessage() +#define VLOG(level) true ? (void)0 : \ + snappy::LogMessageVoidify() & snappy::LogMessage() + +class LogMessage { + public: + LogMessage() { } + ~LogMessage() { + std::cerr << std::endl; + } + + LogMessage& operator<<(const std::string& msg) { + std::cerr << msg; + return *this; + } + LogMessage& operator<<(int x) { + std::cerr << x; + return *this; + } +}; + +// Asserts, both versions activated in debug mode only, +// and ones that are always active. + +#define CRASH_UNLESS(condition) \ + SNAPPY_PREDICT_TRUE(condition) ? (void)0 : \ + snappy::LogMessageVoidify() & snappy::LogMessageCrash() + +#ifdef _MSC_VER +// ~LogMessageCrash calls abort() and therefore never exits. This is by design +// so temporarily disable warning C4722. +#pragma warning(push) +#pragma warning(disable:4722) +#endif + +class LogMessageCrash : public LogMessage { + public: + LogMessageCrash() { } + ~LogMessageCrash() { + std::cerr << std::endl; + abort(); + } +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". + +class LogMessageVoidify { + public: + LogMessageVoidify() { } + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(const LogMessage&) { } +}; + +#define CHECK(cond) CRASH_UNLESS(cond) +#define CHECK_LE(a, b) CRASH_UNLESS((a) <= (b)) +#define CHECK_GE(a, b) CRASH_UNLESS((a) >= (b)) +#define CHECK_EQ(a, b) CRASH_UNLESS((a) == (b)) +#define CHECK_NE(a, b) CRASH_UNLESS((a) != (b)) +#define CHECK_LT(a, b) CRASH_UNLESS((a) < (b)) +#define CHECK_GT(a, b) CRASH_UNLESS((a) > (b)) +#define CHECK_OK(cond) (cond).CheckSuccess() + +} // namespace snappy + +#endif // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_TEST_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy.cc new file mode 100644 index 0000000..fd519e5 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy.cc @@ -0,0 +1,1515 @@ +// Copyright 2005 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "snappy.h" +#include "snappy-internal.h" +#include "snappy-sinksource.h" + +#ifndef SNAPPY_HAVE_SSE2 +#if defined(__SSE2__) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP >= 2) +#define SNAPPY_HAVE_SSE2 1 +#else +#define SNAPPY_HAVE_SSE2 0 +#endif +#endif + +#if SNAPPY_HAVE_SSE2 +#include +#endif +#include + +#include +#include +#include + + +namespace snappy { + +using internal::COPY_1_BYTE_OFFSET; +using internal::COPY_2_BYTE_OFFSET; +using internal::LITERAL; +using internal::char_table; +using internal::kMaximumTagLength; + +// Any hash function will produce a valid compressed bitstream, but a good +// hash function reduces the number of collisions and thus yields better +// compression for compressible input, and more speed for incompressible +// input. Of course, it doesn't hurt if the hash function is reasonably fast +// either, as it gets called a lot. +static inline uint32 HashBytes(uint32 bytes, int shift) { + uint32 kMul = 0x1e35a7bd; + return (bytes * kMul) >> shift; +} +static inline uint32 Hash(const char* p, int shift) { + return HashBytes(UNALIGNED_LOAD32(p), shift); +} + +size_t MaxCompressedLength(size_t source_len) { + // Compressed data can be defined as: + // compressed := item* literal* + // item := literal* copy + // + // The trailing literal sequence has a space blowup of at most 62/60 + // since a literal of length 60 needs one tag byte + one extra byte + // for length information. + // + // Item blowup is trickier to measure. Suppose the "copy" op copies + // 4 bytes of data. Because of a special check in the encoding code, + // we produce a 4-byte copy only if the offset is < 65536. Therefore + // the copy op takes 3 bytes to encode, and this type of item leads + // to at most the 62/60 blowup for representing literals. + // + // Suppose the "copy" op copies 5 bytes of data. If the offset is big + // enough, it will take 5 bytes to encode the copy op. Therefore the + // worst case here is a one-byte literal followed by a five-byte copy. + // I.e., 6 bytes of input turn into 7 bytes of "compressed" data. + // + // This last factor dominates the blowup, so the final estimate is: + return 32 + source_len + source_len/6; +} + +namespace { + +void UnalignedCopy64(const void* src, void* dst) { + char tmp[8]; + memcpy(tmp, src, 8); + memcpy(dst, tmp, 8); +} + +void UnalignedCopy128(const void* src, void* dst) { + // TODO(alkis): Remove this when we upgrade to a recent compiler that emits + // SSE2 moves for memcpy(dst, src, 16). +#if SNAPPY_HAVE_SSE2 + __m128i x = _mm_loadu_si128(static_cast(src)); + _mm_storeu_si128(static_cast<__m128i*>(dst), x); +#else + char tmp[16]; + memcpy(tmp, src, 16); + memcpy(dst, tmp, 16); +#endif +} + +// Copy [src, src+(op_limit-op)) to [op, (op_limit-op)) a byte at a time. Used +// for handling COPY operations where the input and output regions may overlap. +// For example, suppose: +// src == "ab" +// op == src + 2 +// op_limit == op + 20 +// After IncrementalCopySlow(src, op, op_limit), the result will have eleven +// copies of "ab" +// ababababababababababab +// Note that this does not match the semantics of either memcpy() or memmove(). +inline char* IncrementalCopySlow(const char* src, char* op, + char* const op_limit) { + while (op < op_limit) { + *op++ = *src++; + } + return op_limit; +} + +// Copy [src, src+(op_limit-op)) to [op, (op_limit-op)) but faster than +// IncrementalCopySlow. buf_limit is the address past the end of the writable +// region of the buffer. +inline char* IncrementalCopy(const char* src, char* op, char* const op_limit, + char* const buf_limit) { + // Terminology: + // + // slop = buf_limit - op + // pat = op - src + // len = limit - op + assert(src < op); + assert(op_limit <= buf_limit); + // NOTE: The compressor always emits 4 <= len <= 64. It is ok to assume that + // to optimize this function but we have to also handle these cases in case + // the input does not satisfy these conditions. + + size_t pattern_size = op - src; + // The cases are split into different branches to allow the branch predictor, + // FDO, and static prediction hints to work better. For each input we list the + // ratio of invocations that match each condition. + // + // input slop < 16 pat < 8 len > 16 + // ------------------------------------------ + // html|html4|cp 0% 1.01% 27.73% + // urls 0% 0.88% 14.79% + // jpg 0% 64.29% 7.14% + // pdf 0% 2.56% 58.06% + // txt[1-4] 0% 0.23% 0.97% + // pb 0% 0.96% 13.88% + // bin 0.01% 22.27% 41.17% + // + // It is very rare that we don't have enough slop for doing block copies. It + // is also rare that we need to expand a pattern. Small patterns are common + // for incompressible formats and for those we are plenty fast already. + // Lengths are normally not greater than 16 but they vary depending on the + // input. In general if we always predict len <= 16 it would be an ok + // prediction. + // + // In order to be fast we want a pattern >= 8 bytes and an unrolled loop + // copying 2x 8 bytes at a time. + + // Handle the uncommon case where pattern is less than 8 bytes. + if (SNAPPY_PREDICT_FALSE(pattern_size < 8)) { + // Expand pattern to at least 8 bytes. The worse case scenario in terms of + // buffer usage is when the pattern is size 3. ^ is the original position + // of op. x are irrelevant bytes copied by the last UnalignedCopy64. + // + // abc + // abcabcxxxxx + // abcabcabcabcxxxxx + // ^ + // The last x is 14 bytes after ^. + if (SNAPPY_PREDICT_TRUE(op <= buf_limit - 14)) { + while (pattern_size < 8) { + UnalignedCopy64(src, op); + op += pattern_size; + pattern_size *= 2; + } + if (SNAPPY_PREDICT_TRUE(op >= op_limit)) return op_limit; + } else { + return IncrementalCopySlow(src, op, op_limit); + } + } + assert(pattern_size >= 8); + + // Copy 2x 8 bytes at a time. Because op - src can be < 16, a single + // UnalignedCopy128 might overwrite data in op. UnalignedCopy64 is safe + // because expanding the pattern to at least 8 bytes guarantees that + // op - src >= 8. + while (op <= buf_limit - 16) { + UnalignedCopy64(src, op); + UnalignedCopy64(src + 8, op + 8); + src += 16; + op += 16; + if (SNAPPY_PREDICT_TRUE(op >= op_limit)) return op_limit; + } + // We only take this branch if we didn't have enough slop and we can do a + // single 8 byte copy. + if (SNAPPY_PREDICT_FALSE(op <= buf_limit - 8)) { + UnalignedCopy64(src, op); + src += 8; + op += 8; + } + return IncrementalCopySlow(src, op, op_limit); +} + +} // namespace + +static inline char* EmitLiteral(char* op, + const char* literal, + int len, + bool allow_fast_path) { + // The vast majority of copies are below 16 bytes, for which a + // call to memcpy is overkill. This fast path can sometimes + // copy up to 15 bytes too much, but that is okay in the + // main loop, since we have a bit to go on for both sides: + // + // - The input will always have kInputMarginBytes = 15 extra + // available bytes, as long as we're in the main loop, and + // if not, allow_fast_path = false. + // - The output will always have 32 spare bytes (see + // MaxCompressedLength). + assert(len > 0); // Zero-length literals are disallowed + int n = len - 1; + if (allow_fast_path && len <= 16) { + // Fits in tag byte + *op++ = LITERAL | (n << 2); + + UnalignedCopy128(literal, op); + return op + len; + } + + if (n < 60) { + // Fits in tag byte + *op++ = LITERAL | (n << 2); + } else { + // Encode in upcoming bytes + char* base = op; + int count = 0; + op++; + while (n > 0) { + *op++ = n & 0xff; + n >>= 8; + count++; + } + assert(count >= 1); + assert(count <= 4); + *base = LITERAL | ((59+count) << 2); + } + memcpy(op, literal, len); + return op + len; +} + +static inline char* EmitCopyAtMost64(char* op, size_t offset, size_t len, + bool len_less_than_12) { + assert(len <= 64); + assert(len >= 4); + assert(offset < 65536); + assert(len_less_than_12 == (len < 12)); + + if (len_less_than_12 && SNAPPY_PREDICT_TRUE(offset < 2048)) { + // offset fits in 11 bits. The 3 highest go in the top of the first byte, + // and the rest go in the second byte. + *op++ = COPY_1_BYTE_OFFSET + ((len - 4) << 2) + ((offset >> 3) & 0xe0); + *op++ = offset & 0xff; + } else { + // Write 4 bytes, though we only care about 3 of them. The output buffer + // is required to have some slack, so the extra byte won't overrun it. + uint32 u = COPY_2_BYTE_OFFSET + ((len - 1) << 2) + (offset << 8); + LittleEndian::Store32(op, u); + op += 3; + } + return op; +} + +static inline char* EmitCopy(char* op, size_t offset, size_t len, + bool len_less_than_12) { + assert(len_less_than_12 == (len < 12)); + if (len_less_than_12) { + return EmitCopyAtMost64(op, offset, len, true); + } else { + // A special case for len <= 64 might help, but so far measurements suggest + // it's in the noise. + + // Emit 64 byte copies but make sure to keep at least four bytes reserved. + while (SNAPPY_PREDICT_FALSE(len >= 68)) { + op = EmitCopyAtMost64(op, offset, 64, false); + len -= 64; + } + + // One or two copies will now finish the job. + if (len > 64) { + op = EmitCopyAtMost64(op, offset, 60, false); + len -= 60; + } + + // Emit remainder. + op = EmitCopyAtMost64(op, offset, len, len < 12); + return op; + } +} + +bool GetUncompressedLength(const char* start, size_t n, size_t* result) { + uint32 v = 0; + const char* limit = start + n; + if (Varint::Parse32WithLimit(start, limit, &v) != NULL) { + *result = v; + return true; + } else { + return false; + } +} + +namespace internal { +uint16* WorkingMemory::GetHashTable(size_t input_size, int* table_size) { + // Use smaller hash table when input.size() is smaller, since we + // fill the table, incurring O(hash table size) overhead for + // compression, and if the input is short, we won't need that + // many hash table entries anyway. + assert(kMaxHashTableSize >= 256); + size_t htsize = 256; + while (htsize < kMaxHashTableSize && htsize < input_size) { + htsize <<= 1; + } + + uint16* table; + if (htsize <= ARRAYSIZE(small_table_)) { + table = small_table_; + } else { + if (large_table_ == NULL) { + large_table_ = new uint16[kMaxHashTableSize]; + } + table = large_table_; + } + + *table_size = htsize; + memset(table, 0, htsize * sizeof(*table)); + return table; +} +} // end namespace internal + +// For 0 <= offset <= 4, GetUint32AtOffset(GetEightBytesAt(p), offset) will +// equal UNALIGNED_LOAD32(p + offset). Motivation: On x86-64 hardware we have +// empirically found that overlapping loads such as +// UNALIGNED_LOAD32(p) ... UNALIGNED_LOAD32(p+1) ... UNALIGNED_LOAD32(p+2) +// are slower than UNALIGNED_LOAD64(p) followed by shifts and casts to uint32. +// +// We have different versions for 64- and 32-bit; ideally we would avoid the +// two functions and just inline the UNALIGNED_LOAD64 call into +// GetUint32AtOffset, but GCC (at least not as of 4.6) is seemingly not clever +// enough to avoid loading the value multiple times then. For 64-bit, the load +// is done when GetEightBytesAt() is called, whereas for 32-bit, the load is +// done at GetUint32AtOffset() time. + +#ifdef ARCH_K8 + +typedef uint64 EightBytesReference; + +static inline EightBytesReference GetEightBytesAt(const char* ptr) { + return UNALIGNED_LOAD64(ptr); +} + +static inline uint32 GetUint32AtOffset(uint64 v, int offset) { + assert(offset >= 0); + assert(offset <= 4); + return v >> (LittleEndian::IsLittleEndian() ? 8 * offset : 32 - 8 * offset); +} + +#else + +typedef const char* EightBytesReference; + +static inline EightBytesReference GetEightBytesAt(const char* ptr) { + return ptr; +} + +static inline uint32 GetUint32AtOffset(const char* v, int offset) { + assert(offset >= 0); + assert(offset <= 4); + return UNALIGNED_LOAD32(v + offset); +} + +#endif + +// Flat array compression that does not emit the "uncompressed length" +// prefix. Compresses "input" string to the "*op" buffer. +// +// REQUIRES: "input" is at most "kBlockSize" bytes long. +// REQUIRES: "op" points to an array of memory that is at least +// "MaxCompressedLength(input.size())" in size. +// REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. +// REQUIRES: "table_size" is a power of two +// +// Returns an "end" pointer into "op" buffer. +// "end - op" is the compressed size of "input". +namespace internal { +char* CompressFragment(const char* input, + size_t input_size, + char* op, + uint16* table, + const int table_size) { + // "ip" is the input pointer, and "op" is the output pointer. + const char* ip = input; + assert(input_size <= kBlockSize); + assert((table_size & (table_size - 1)) == 0); // table must be power of two + const int shift = 32 - Bits::Log2Floor(table_size); + assert(static_cast(kuint32max >> shift) == table_size - 1); + const char* ip_end = input + input_size; + const char* base_ip = ip; + // Bytes in [next_emit, ip) will be emitted as literal bytes. Or + // [next_emit, ip_end) after the main loop. + const char* next_emit = ip; + + const size_t kInputMarginBytes = 15; + if (SNAPPY_PREDICT_TRUE(input_size >= kInputMarginBytes)) { + const char* ip_limit = input + input_size - kInputMarginBytes; + + for (uint32 next_hash = Hash(++ip, shift); ; ) { + assert(next_emit < ip); + // The body of this loop calls EmitLiteral once and then EmitCopy one or + // more times. (The exception is that when we're close to exhausting + // the input we goto emit_remainder.) + // + // In the first iteration of this loop we're just starting, so + // there's nothing to copy, so calling EmitLiteral once is + // necessary. And we only start a new iteration when the + // current iteration has determined that a call to EmitLiteral will + // precede the next call to EmitCopy (if any). + // + // Step 1: Scan forward in the input looking for a 4-byte-long match. + // If we get close to exhausting the input then goto emit_remainder. + // + // Heuristic match skipping: If 32 bytes are scanned with no matches + // found, start looking only at every other byte. If 32 more bytes are + // scanned (or skipped), look at every third byte, etc.. When a match is + // found, immediately go back to looking at every byte. This is a small + // loss (~5% performance, ~0.1% density) for compressible data due to more + // bookkeeping, but for non-compressible data (such as JPEG) it's a huge + // win since the compressor quickly "realizes" the data is incompressible + // and doesn't bother looking for matches everywhere. + // + // The "skip" variable keeps track of how many bytes there are since the + // last match; dividing it by 32 (ie. right-shifting by five) gives the + // number of bytes to move ahead for each iteration. + uint32 skip = 32; + + const char* next_ip = ip; + const char* candidate; + do { + ip = next_ip; + uint32 hash = next_hash; + assert(hash == Hash(ip, shift)); + uint32 bytes_between_hash_lookups = skip >> 5; + skip += bytes_between_hash_lookups; + next_ip = ip + bytes_between_hash_lookups; + if (SNAPPY_PREDICT_FALSE(next_ip > ip_limit)) { + goto emit_remainder; + } + next_hash = Hash(next_ip, shift); + candidate = base_ip + table[hash]; + assert(candidate >= base_ip); + assert(candidate < ip); + + table[hash] = ip - base_ip; + } while (SNAPPY_PREDICT_TRUE(UNALIGNED_LOAD32(ip) != + UNALIGNED_LOAD32(candidate))); + + // Step 2: A 4-byte match has been found. We'll later see if more + // than 4 bytes match. But, prior to the match, input + // bytes [next_emit, ip) are unmatched. Emit them as "literal bytes." + assert(next_emit + 16 <= ip_end); + op = EmitLiteral(op, next_emit, ip - next_emit, true); + + // Step 3: Call EmitCopy, and then see if another EmitCopy could + // be our next move. Repeat until we find no match for the + // input immediately after what was consumed by the last EmitCopy call. + // + // If we exit this loop normally then we need to call EmitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can exit + // this loop via goto if we get close to exhausting the input. + EightBytesReference input_bytes; + uint32 candidate_bytes = 0; + + do { + // We have a 4-byte match at ip, and no need to emit any + // "literal bytes" prior to ip. + const char* base = ip; + std::pair p = + FindMatchLength(candidate + 4, ip + 4, ip_end); + size_t matched = 4 + p.first; + ip += matched; + size_t offset = base - candidate; + assert(0 == memcmp(base, candidate, matched)); + op = EmitCopy(op, offset, matched, p.second); + next_emit = ip; + if (SNAPPY_PREDICT_FALSE(ip >= ip_limit)) { + goto emit_remainder; + } + // We are now looking for a 4-byte match again. We read + // table[Hash(ip, shift)] for that. To improve compression, + // we also update table[Hash(ip - 1, shift)] and table[Hash(ip, shift)]. + input_bytes = GetEightBytesAt(ip - 1); + uint32 prev_hash = HashBytes(GetUint32AtOffset(input_bytes, 0), shift); + table[prev_hash] = ip - base_ip - 1; + uint32 cur_hash = HashBytes(GetUint32AtOffset(input_bytes, 1), shift); + candidate = base_ip + table[cur_hash]; + candidate_bytes = UNALIGNED_LOAD32(candidate); + table[cur_hash] = ip - base_ip; + } while (GetUint32AtOffset(input_bytes, 1) == candidate_bytes); + + next_hash = HashBytes(GetUint32AtOffset(input_bytes, 2), shift); + ++ip; + } + } + + emit_remainder: + // Emit the remaining bytes as a literal + if (next_emit < ip_end) { + op = EmitLiteral(op, next_emit, ip_end - next_emit, false); + } + + return op; +} +} // end namespace internal + +// Called back at avery compression call to trace parameters and sizes. +static inline void Report(const char *algorithm, size_t compressed_size, + size_t uncompressed_size) {} + +// Signature of output types needed by decompression code. +// The decompression code is templatized on a type that obeys this +// signature so that we do not pay virtual function call overhead in +// the middle of a tight decompression loop. +// +// class DecompressionWriter { +// public: +// // Called before decompression +// void SetExpectedLength(size_t length); +// +// // Called after decompression +// bool CheckLength() const; +// +// // Called repeatedly during decompression +// bool Append(const char* ip, size_t length); +// bool AppendFromSelf(uint32 offset, size_t length); +// +// // The rules for how TryFastAppend differs from Append are somewhat +// // convoluted: +// // +// // - TryFastAppend is allowed to decline (return false) at any +// // time, for any reason -- just "return false" would be +// // a perfectly legal implementation of TryFastAppend. +// // The intention is for TryFastAppend to allow a fast path +// // in the common case of a small append. +// // - TryFastAppend is allowed to read up to bytes +// // from the input buffer, whereas Append is allowed to read +// // . However, if it returns true, it must leave +// // at least five (kMaximumTagLength) bytes in the input buffer +// // afterwards, so that there is always enough space to read the +// // next tag without checking for a refill. +// // - TryFastAppend must always return decline (return false) +// // if is 61 or more, as in this case the literal length is not +// // decoded fully. In practice, this should not be a big problem, +// // as it is unlikely that one would implement a fast path accepting +// // this much data. +// // +// bool TryFastAppend(const char* ip, size_t available, size_t length); +// }; + +namespace internal { + +// Mapping from i in range [0,4] to a mask to extract the bottom 8*i bits +static const uint32 wordmask[] = { + 0u, 0xffu, 0xffffu, 0xffffffu, 0xffffffffu +}; + +} // end namespace internal + +// Helper class for decompression +class SnappyDecompressor { + private: + Source* reader_; // Underlying source of bytes to decompress + const char* ip_; // Points to next buffered byte + const char* ip_limit_; // Points just past buffered bytes + uint32 peeked_; // Bytes peeked from reader (need to skip) + bool eof_; // Hit end of input without an error? + char scratch_[kMaximumTagLength]; // See RefillTag(). + + // Ensure that all of the tag metadata for the next tag is available + // in [ip_..ip_limit_-1]. Also ensures that [ip,ip+4] is readable even + // if (ip_limit_ - ip_ < 5). + // + // Returns true on success, false on error or end of input. + bool RefillTag(); + + public: + explicit SnappyDecompressor(Source* reader) + : reader_(reader), + ip_(NULL), + ip_limit_(NULL), + peeked_(0), + eof_(false) { + } + + ~SnappyDecompressor() { + // Advance past any bytes we peeked at from the reader + reader_->Skip(peeked_); + } + + // Returns true iff we have hit the end of the input without an error. + bool eof() const { + return eof_; + } + + // Read the uncompressed length stored at the start of the compressed data. + // On succcess, stores the length in *result and returns true. + // On failure, returns false. + bool ReadUncompressedLength(uint32* result) { + assert(ip_ == NULL); // Must not have read anything yet + // Length is encoded in 1..5 bytes + *result = 0; + uint32 shift = 0; + while (true) { + if (shift >= 32) return false; + size_t n; + const char* ip = reader_->Peek(&n); + if (n == 0) return false; + const unsigned char c = *(reinterpret_cast(ip)); + reader_->Skip(1); + uint32 val = c & 0x7f; + if (((val << shift) >> shift) != val) return false; + *result |= val << shift; + if (c < 128) { + break; + } + shift += 7; + } + return true; + } + + // Process the next item found in the input. + // Returns true if successful, false on error or end of input. + template + void DecompressAllTags(Writer* writer) { + const char* ip = ip_; + // For position-independent executables, accessing global arrays can be + // slow. Move wordmask array onto the stack to mitigate this. + uint32 wordmask[sizeof(internal::wordmask)/sizeof(uint32)]; + // Do not use memcpy to copy internal::wordmask to + // wordmask. LLVM converts stack arrays to global arrays if it detects + // const stack arrays and this hurts the performance of position + // independent code. This change is temporary and can be reverted when + // https://reviews.llvm.org/D30759 is approved. + wordmask[0] = internal::wordmask[0]; + wordmask[1] = internal::wordmask[1]; + wordmask[2] = internal::wordmask[2]; + wordmask[3] = internal::wordmask[3]; + wordmask[4] = internal::wordmask[4]; + + // We could have put this refill fragment only at the beginning of the loop. + // However, duplicating it at the end of each branch gives the compiler more + // scope to optimize the expression based on the local + // context, which overall increases speed. + #define MAYBE_REFILL() \ + if (ip_limit_ - ip < kMaximumTagLength) { \ + ip_ = ip; \ + if (!RefillTag()) return; \ + ip = ip_; \ + } + + MAYBE_REFILL(); + // Add loop alignment directive. Without this directive, we observed + // significant performance degradation on several intel architectures + // in snappy benchmark built with LLVM. The degradation was caused by + // increased branch miss prediction. +#if defined(__clang__) && defined(__x86_64__) + asm volatile (".p2align 5"); +#endif + for ( ;; ) { + const unsigned char c = *(reinterpret_cast(ip++)); + + // Ratio of iterations that have LITERAL vs non-LITERAL for different + // inputs. + // + // input LITERAL NON_LITERAL + // ----------------------------------- + // html|html4|cp 23% 77% + // urls 36% 64% + // jpg 47% 53% + // pdf 19% 81% + // txt[1-4] 25% 75% + // pb 24% 76% + // bin 24% 76% + if (SNAPPY_PREDICT_FALSE((c & 0x3) == LITERAL)) { + size_t literal_length = (c >> 2) + 1u; + if (writer->TryFastAppend(ip, ip_limit_ - ip, literal_length)) { + assert(literal_length < 61); + ip += literal_length; + // NOTE(user): There is no MAYBE_REFILL() here, as TryFastAppend() + // will not return true unless there's already at least five spare + // bytes in addition to the literal. + continue; + } + if (SNAPPY_PREDICT_FALSE(literal_length >= 61)) { + // Long literal. + const size_t literal_length_length = literal_length - 60; + literal_length = + (LittleEndian::Load32(ip) & wordmask[literal_length_length]) + 1; + ip += literal_length_length; + } + + size_t avail = ip_limit_ - ip; + while (avail < literal_length) { + if (!writer->Append(ip, avail)) return; + literal_length -= avail; + reader_->Skip(peeked_); + size_t n; + ip = reader_->Peek(&n); + avail = n; + peeked_ = avail; + if (avail == 0) return; // Premature end of input + ip_limit_ = ip + avail; + } + if (!writer->Append(ip, literal_length)) { + return; + } + ip += literal_length; + MAYBE_REFILL(); + } else { + const size_t entry = char_table[c]; + const size_t trailer = LittleEndian::Load32(ip) & wordmask[entry >> 11]; + const size_t length = entry & 0xff; + ip += entry >> 11; + + // copy_offset/256 is encoded in bits 8..10. By just fetching + // those bits, we get copy_offset (since the bit-field starts at + // bit 8). + const size_t copy_offset = entry & 0x700; + if (!writer->AppendFromSelf(copy_offset + trailer, length)) { + return; + } + MAYBE_REFILL(); + } + } + +#undef MAYBE_REFILL + } +}; + +bool SnappyDecompressor::RefillTag() { + const char* ip = ip_; + if (ip == ip_limit_) { + // Fetch a new fragment from the reader + reader_->Skip(peeked_); // All peeked bytes are used up + size_t n; + ip = reader_->Peek(&n); + peeked_ = n; + eof_ = (n == 0); + if (eof_) return false; + ip_limit_ = ip + n; + } + + // Read the tag character + assert(ip < ip_limit_); + const unsigned char c = *(reinterpret_cast(ip)); + const uint32 entry = char_table[c]; + const uint32 needed = (entry >> 11) + 1; // +1 byte for 'c' + assert(needed <= sizeof(scratch_)); + + // Read more bytes from reader if needed + uint32 nbuf = ip_limit_ - ip; + if (nbuf < needed) { + // Stitch together bytes from ip and reader to form the word + // contents. We store the needed bytes in "scratch_". They + // will be consumed immediately by the caller since we do not + // read more than we need. + memmove(scratch_, ip, nbuf); + reader_->Skip(peeked_); // All peeked bytes are used up + peeked_ = 0; + while (nbuf < needed) { + size_t length; + const char* src = reader_->Peek(&length); + if (length == 0) return false; + uint32 to_add = std::min(needed - nbuf, length); + memcpy(scratch_ + nbuf, src, to_add); + nbuf += to_add; + reader_->Skip(to_add); + } + assert(nbuf == needed); + ip_ = scratch_; + ip_limit_ = scratch_ + needed; + } else if (nbuf < kMaximumTagLength) { + // Have enough bytes, but move into scratch_ so that we do not + // read past end of input + memmove(scratch_, ip, nbuf); + reader_->Skip(peeked_); // All peeked bytes are used up + peeked_ = 0; + ip_ = scratch_; + ip_limit_ = scratch_ + nbuf; + } else { + // Pass pointer to buffer returned by reader_. + ip_ = ip; + } + return true; +} + +template +static bool InternalUncompress(Source* r, Writer* writer) { + // Read the uncompressed length from the front of the compressed input + SnappyDecompressor decompressor(r); + uint32 uncompressed_len = 0; + if (!decompressor.ReadUncompressedLength(&uncompressed_len)) return false; + + return InternalUncompressAllTags(&decompressor, writer, r->Available(), + uncompressed_len); +} + +template +static bool InternalUncompressAllTags(SnappyDecompressor* decompressor, + Writer* writer, + uint32 compressed_len, + uint32 uncompressed_len) { + Report("snappy_uncompress", compressed_len, uncompressed_len); + + writer->SetExpectedLength(uncompressed_len); + + // Process the entire input + decompressor->DecompressAllTags(writer); + writer->Flush(); + return (decompressor->eof() && writer->CheckLength()); +} + +bool GetUncompressedLength(Source* source, uint32* result) { + SnappyDecompressor decompressor(source); + return decompressor.ReadUncompressedLength(result); +} + +size_t Compress(Source* reader, Sink* writer) { + size_t written = 0; + size_t N = reader->Available(); + const size_t uncompressed_size = N; + char ulength[Varint::kMax32]; + char* p = Varint::Encode32(ulength, N); + writer->Append(ulength, p-ulength); + written += (p - ulength); + + internal::WorkingMemory wmem; + char* scratch = NULL; + char* scratch_output = NULL; + + while (N > 0) { + // Get next block to compress (without copying if possible) + size_t fragment_size; + const char* fragment = reader->Peek(&fragment_size); + assert(fragment_size != 0); // premature end of input + const size_t num_to_read = std::min(N, kBlockSize); + size_t bytes_read = fragment_size; + + size_t pending_advance = 0; + if (bytes_read >= num_to_read) { + // Buffer returned by reader is large enough + pending_advance = num_to_read; + fragment_size = num_to_read; + } else { + // Read into scratch buffer + if (scratch == NULL) { + // If this is the last iteration, we want to allocate N bytes + // of space, otherwise the max possible kBlockSize space. + // num_to_read contains exactly the correct value + scratch = new char[num_to_read]; + } + memcpy(scratch, fragment, bytes_read); + reader->Skip(bytes_read); + + while (bytes_read < num_to_read) { + fragment = reader->Peek(&fragment_size); + size_t n = std::min(fragment_size, num_to_read - bytes_read); + memcpy(scratch + bytes_read, fragment, n); + bytes_read += n; + reader->Skip(n); + } + assert(bytes_read == num_to_read); + fragment = scratch; + fragment_size = num_to_read; + } + assert(fragment_size == num_to_read); + + // Get encoding table for compression + int table_size; + uint16* table = wmem.GetHashTable(num_to_read, &table_size); + + // Compress input_fragment and append to dest + const int max_output = MaxCompressedLength(num_to_read); + + // Need a scratch buffer for the output, in case the byte sink doesn't + // have room for us directly. + if (scratch_output == NULL) { + scratch_output = new char[max_output]; + } else { + // Since we encode kBlockSize regions followed by a region + // which is <= kBlockSize in length, a previously allocated + // scratch_output[] region is big enough for this iteration. + } + char* dest = writer->GetAppendBuffer(max_output, scratch_output); + char* end = internal::CompressFragment(fragment, fragment_size, + dest, table, table_size); + writer->Append(dest, end - dest); + written += (end - dest); + + N -= num_to_read; + reader->Skip(pending_advance); + } + + Report("snappy_compress", written, uncompressed_size); + + delete[] scratch; + delete[] scratch_output; + + return written; +} + +// ----------------------------------------------------------------------- +// IOVec interfaces +// ----------------------------------------------------------------------- + +// A type that writes to an iovec. +// Note that this is not a "ByteSink", but a type that matches the +// Writer template argument to SnappyDecompressor::DecompressAllTags(). +class SnappyIOVecWriter { + private: + const struct iovec* output_iov_; + const size_t output_iov_count_; + + // We are currently writing into output_iov_[curr_iov_index_]. + size_t curr_iov_index_; + + // Bytes written to output_iov_[curr_iov_index_] so far. + size_t curr_iov_written_; + + // Total bytes decompressed into output_iov_ so far. + size_t total_written_; + + // Maximum number of bytes that will be decompressed into output_iov_. + size_t output_limit_; + + inline char* GetIOVecPointer(size_t index, size_t offset) { + return reinterpret_cast(output_iov_[index].iov_base) + + offset; + } + + public: + // Does not take ownership of iov. iov must be valid during the + // entire lifetime of the SnappyIOVecWriter. + inline SnappyIOVecWriter(const struct iovec* iov, size_t iov_count) + : output_iov_(iov), + output_iov_count_(iov_count), + curr_iov_index_(0), + curr_iov_written_(0), + total_written_(0), + output_limit_(-1) { + } + + inline void SetExpectedLength(size_t len) { + output_limit_ = len; + } + + inline bool CheckLength() const { + return total_written_ == output_limit_; + } + + inline bool Append(const char* ip, size_t len) { + if (total_written_ + len > output_limit_) { + return false; + } + + while (len > 0) { + assert(curr_iov_written_ <= output_iov_[curr_iov_index_].iov_len); + if (curr_iov_written_ >= output_iov_[curr_iov_index_].iov_len) { + // This iovec is full. Go to the next one. + if (curr_iov_index_ + 1 >= output_iov_count_) { + return false; + } + curr_iov_written_ = 0; + ++curr_iov_index_; + } + + const size_t to_write = std::min( + len, output_iov_[curr_iov_index_].iov_len - curr_iov_written_); + memcpy(GetIOVecPointer(curr_iov_index_, curr_iov_written_), + ip, + to_write); + curr_iov_written_ += to_write; + total_written_ += to_write; + ip += to_write; + len -= to_write; + } + + return true; + } + + inline bool TryFastAppend(const char* ip, size_t available, size_t len) { + const size_t space_left = output_limit_ - total_written_; + if (len <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16 && + output_iov_[curr_iov_index_].iov_len - curr_iov_written_ >= 16) { + // Fast path, used for the majority (about 95%) of invocations. + char* ptr = GetIOVecPointer(curr_iov_index_, curr_iov_written_); + UnalignedCopy128(ip, ptr); + curr_iov_written_ += len; + total_written_ += len; + return true; + } + + return false; + } + + inline bool AppendFromSelf(size_t offset, size_t len) { + if (offset > total_written_ || offset == 0) { + return false; + } + const size_t space_left = output_limit_ - total_written_; + if (len > space_left) { + return false; + } + + // Locate the iovec from which we need to start the copy. + size_t from_iov_index = curr_iov_index_; + size_t from_iov_offset = curr_iov_written_; + while (offset > 0) { + if (from_iov_offset >= offset) { + from_iov_offset -= offset; + break; + } + + offset -= from_iov_offset; + assert(from_iov_index > 0); + --from_iov_index; + from_iov_offset = output_iov_[from_iov_index].iov_len; + } + + // Copy bytes starting from the iovec pointed to by from_iov_index to + // the current iovec. + while (len > 0) { + assert(from_iov_index <= curr_iov_index_); + if (from_iov_index != curr_iov_index_) { + const size_t to_copy = std::min( + output_iov_[from_iov_index].iov_len - from_iov_offset, + len); + Append(GetIOVecPointer(from_iov_index, from_iov_offset), to_copy); + len -= to_copy; + if (len > 0) { + ++from_iov_index; + from_iov_offset = 0; + } + } else { + assert(curr_iov_written_ <= output_iov_[curr_iov_index_].iov_len); + size_t to_copy = std::min(output_iov_[curr_iov_index_].iov_len - + curr_iov_written_, + len); + if (to_copy == 0) { + // This iovec is full. Go to the next one. + if (curr_iov_index_ + 1 >= output_iov_count_) { + return false; + } + ++curr_iov_index_; + curr_iov_written_ = 0; + continue; + } + if (to_copy > len) { + to_copy = len; + } + IncrementalCopySlow( + GetIOVecPointer(from_iov_index, from_iov_offset), + GetIOVecPointer(curr_iov_index_, curr_iov_written_), + GetIOVecPointer(curr_iov_index_, curr_iov_written_) + to_copy); + curr_iov_written_ += to_copy; + from_iov_offset += to_copy; + total_written_ += to_copy; + len -= to_copy; + } + } + + return true; + } + + inline void Flush() {} +}; + +bool RawUncompressToIOVec(const char* compressed, size_t compressed_length, + const struct iovec* iov, size_t iov_cnt) { + ByteArraySource reader(compressed, compressed_length); + return RawUncompressToIOVec(&reader, iov, iov_cnt); +} + +bool RawUncompressToIOVec(Source* compressed, const struct iovec* iov, + size_t iov_cnt) { + SnappyIOVecWriter output(iov, iov_cnt); + return InternalUncompress(compressed, &output); +} + +// ----------------------------------------------------------------------- +// Flat array interfaces +// ----------------------------------------------------------------------- + +// A type that writes to a flat array. +// Note that this is not a "ByteSink", but a type that matches the +// Writer template argument to SnappyDecompressor::DecompressAllTags(). +class SnappyArrayWriter { + private: + char* base_; + char* op_; + char* op_limit_; + + public: + inline explicit SnappyArrayWriter(char* dst) + : base_(dst), + op_(dst), + op_limit_(dst) { + } + + inline void SetExpectedLength(size_t len) { + op_limit_ = op_ + len; + } + + inline bool CheckLength() const { + return op_ == op_limit_; + } + + inline bool Append(const char* ip, size_t len) { + char* op = op_; + const size_t space_left = op_limit_ - op; + if (space_left < len) { + return false; + } + memcpy(op, ip, len); + op_ = op + len; + return true; + } + + inline bool TryFastAppend(const char* ip, size_t available, size_t len) { + char* op = op_; + const size_t space_left = op_limit_ - op; + if (len <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16) { + // Fast path, used for the majority (about 95%) of invocations. + UnalignedCopy128(ip, op); + op_ = op + len; + return true; + } else { + return false; + } + } + + inline bool AppendFromSelf(size_t offset, size_t len) { + char* const op_end = op_ + len; + + // Check if we try to append from before the start of the buffer. + // Normally this would just be a check for "produced < offset", + // but "produced <= offset - 1u" is equivalent for every case + // except the one where offset==0, where the right side will wrap around + // to a very big number. This is convenient, as offset==0 is another + // invalid case that we also want to catch, so that we do not go + // into an infinite loop. + if (Produced() <= offset - 1u || op_end > op_limit_) return false; + op_ = IncrementalCopy(op_ - offset, op_, op_end, op_limit_); + + return true; + } + inline size_t Produced() const { + assert(op_ >= base_); + return op_ - base_; + } + inline void Flush() {} +}; + +bool RawUncompress(const char* compressed, size_t n, char* uncompressed) { + ByteArraySource reader(compressed, n); + return RawUncompress(&reader, uncompressed); +} + +bool RawUncompress(Source* compressed, char* uncompressed) { + SnappyArrayWriter output(uncompressed); + return InternalUncompress(compressed, &output); +} + +bool Uncompress(const char* compressed, size_t n, string* uncompressed) { + size_t ulength; + if (!GetUncompressedLength(compressed, n, &ulength)) { + return false; + } + // On 32-bit builds: max_size() < kuint32max. Check for that instead + // of crashing (e.g., consider externally specified compressed data). + if (ulength > uncompressed->max_size()) { + return false; + } + STLStringResizeUninitialized(uncompressed, ulength); + return RawUncompress(compressed, n, string_as_array(uncompressed)); +} + +// A Writer that drops everything on the floor and just does validation +class SnappyDecompressionValidator { + private: + size_t expected_; + size_t produced_; + + public: + inline SnappyDecompressionValidator() : expected_(0), produced_(0) { } + inline void SetExpectedLength(size_t len) { + expected_ = len; + } + inline bool CheckLength() const { + return expected_ == produced_; + } + inline bool Append(const char* ip, size_t len) { + produced_ += len; + return produced_ <= expected_; + } + inline bool TryFastAppend(const char* ip, size_t available, size_t length) { + return false; + } + inline bool AppendFromSelf(size_t offset, size_t len) { + // See SnappyArrayWriter::AppendFromSelf for an explanation of + // the "offset - 1u" trick. + if (produced_ <= offset - 1u) return false; + produced_ += len; + return produced_ <= expected_; + } + inline void Flush() {} +}; + +bool IsValidCompressedBuffer(const char* compressed, size_t n) { + ByteArraySource reader(compressed, n); + SnappyDecompressionValidator writer; + return InternalUncompress(&reader, &writer); +} + +bool IsValidCompressed(Source* compressed) { + SnappyDecompressionValidator writer; + return InternalUncompress(compressed, &writer); +} + +void RawCompress(const char* input, + size_t input_length, + char* compressed, + size_t* compressed_length) { + ByteArraySource reader(input, input_length); + UncheckedByteArraySink writer(compressed); + Compress(&reader, &writer); + + // Compute how many bytes were added + *compressed_length = (writer.CurrentDestination() - compressed); +} + +size_t Compress(const char* input, size_t input_length, string* compressed) { + // Pre-grow the buffer to the max length of the compressed output + STLStringResizeUninitialized(compressed, MaxCompressedLength(input_length)); + + size_t compressed_length; + RawCompress(input, input_length, string_as_array(compressed), + &compressed_length); + compressed->resize(compressed_length); + return compressed_length; +} + +// ----------------------------------------------------------------------- +// Sink interface +// ----------------------------------------------------------------------- + +// A type that decompresses into a Sink. The template parameter +// Allocator must export one method "char* Allocate(int size);", which +// allocates a buffer of "size" and appends that to the destination. +template +class SnappyScatteredWriter { + Allocator allocator_; + + // We need random access into the data generated so far. Therefore + // we keep track of all of the generated data as an array of blocks. + // All of the blocks except the last have length kBlockSize. + std::vector blocks_; + size_t expected_; + + // Total size of all fully generated blocks so far + size_t full_size_; + + // Pointer into current output block + char* op_base_; // Base of output block + char* op_ptr_; // Pointer to next unfilled byte in block + char* op_limit_; // Pointer just past block + + inline size_t Size() const { + return full_size_ + (op_ptr_ - op_base_); + } + + bool SlowAppend(const char* ip, size_t len); + bool SlowAppendFromSelf(size_t offset, size_t len); + + public: + inline explicit SnappyScatteredWriter(const Allocator& allocator) + : allocator_(allocator), + full_size_(0), + op_base_(NULL), + op_ptr_(NULL), + op_limit_(NULL) { + } + + inline void SetExpectedLength(size_t len) { + assert(blocks_.empty()); + expected_ = len; + } + + inline bool CheckLength() const { + return Size() == expected_; + } + + // Return the number of bytes actually uncompressed so far + inline size_t Produced() const { + return Size(); + } + + inline bool Append(const char* ip, size_t len) { + size_t avail = op_limit_ - op_ptr_; + if (len <= avail) { + // Fast path + memcpy(op_ptr_, ip, len); + op_ptr_ += len; + return true; + } else { + return SlowAppend(ip, len); + } + } + + inline bool TryFastAppend(const char* ip, size_t available, size_t length) { + char* op = op_ptr_; + const int space_left = op_limit_ - op; + if (length <= 16 && available >= 16 + kMaximumTagLength && + space_left >= 16) { + // Fast path, used for the majority (about 95%) of invocations. + UnalignedCopy128(ip, op); + op_ptr_ = op + length; + return true; + } else { + return false; + } + } + + inline bool AppendFromSelf(size_t offset, size_t len) { + char* const op_end = op_ptr_ + len; + // See SnappyArrayWriter::AppendFromSelf for an explanation of + // the "offset - 1u" trick. + if (SNAPPY_PREDICT_TRUE(offset - 1u < op_ptr_ - op_base_ && + op_end <= op_limit_)) { + // Fast path: src and dst in current block. + op_ptr_ = IncrementalCopy(op_ptr_ - offset, op_ptr_, op_end, op_limit_); + return true; + } + return SlowAppendFromSelf(offset, len); + } + + // Called at the end of the decompress. We ask the allocator + // write all blocks to the sink. + inline void Flush() { allocator_.Flush(Produced()); } +}; + +template +bool SnappyScatteredWriter::SlowAppend(const char* ip, size_t len) { + size_t avail = op_limit_ - op_ptr_; + while (len > avail) { + // Completely fill this block + memcpy(op_ptr_, ip, avail); + op_ptr_ += avail; + assert(op_limit_ - op_ptr_ == 0); + full_size_ += (op_ptr_ - op_base_); + len -= avail; + ip += avail; + + // Bounds check + if (full_size_ + len > expected_) { + return false; + } + + // Make new block + size_t bsize = std::min(kBlockSize, expected_ - full_size_); + op_base_ = allocator_.Allocate(bsize); + op_ptr_ = op_base_; + op_limit_ = op_base_ + bsize; + blocks_.push_back(op_base_); + avail = bsize; + } + + memcpy(op_ptr_, ip, len); + op_ptr_ += len; + return true; +} + +template +bool SnappyScatteredWriter::SlowAppendFromSelf(size_t offset, + size_t len) { + // Overflow check + // See SnappyArrayWriter::AppendFromSelf for an explanation of + // the "offset - 1u" trick. + const size_t cur = Size(); + if (offset - 1u >= cur) return false; + if (expected_ - cur < len) return false; + + // Currently we shouldn't ever hit this path because Compress() chops the + // input into blocks and does not create cross-block copies. However, it is + // nice if we do not rely on that, since we can get better compression if we + // allow cross-block copies and thus might want to change the compressor in + // the future. + size_t src = cur - offset; + while (len-- > 0) { + char c = blocks_[src >> kBlockLog][src & (kBlockSize-1)]; + Append(&c, 1); + src++; + } + return true; +} + +class SnappySinkAllocator { + public: + explicit SnappySinkAllocator(Sink* dest): dest_(dest) {} + ~SnappySinkAllocator() {} + + char* Allocate(int size) { + Datablock block(new char[size], size); + blocks_.push_back(block); + return block.data; + } + + // We flush only at the end, because the writer wants + // random access to the blocks and once we hand the + // block over to the sink, we can't access it anymore. + // Also we don't write more than has been actually written + // to the blocks. + void Flush(size_t size) { + size_t size_written = 0; + size_t block_size; + for (int i = 0; i < blocks_.size(); ++i) { + block_size = std::min(blocks_[i].size, size - size_written); + dest_->AppendAndTakeOwnership(blocks_[i].data, block_size, + &SnappySinkAllocator::Deleter, NULL); + size_written += block_size; + } + blocks_.clear(); + } + + private: + struct Datablock { + char* data; + size_t size; + Datablock(char* p, size_t s) : data(p), size(s) {} + }; + + static void Deleter(void* arg, const char* bytes, size_t size) { + delete[] bytes; + } + + Sink* dest_; + std::vector blocks_; + + // Note: copying this object is allowed +}; + +size_t UncompressAsMuchAsPossible(Source* compressed, Sink* uncompressed) { + SnappySinkAllocator allocator(uncompressed); + SnappyScatteredWriter writer(allocator); + InternalUncompress(compressed, &writer); + return writer.Produced(); +} + +bool Uncompress(Source* compressed, Sink* uncompressed) { + // Read the uncompressed length from the front of the compressed input + SnappyDecompressor decompressor(compressed); + uint32 uncompressed_len = 0; + if (!decompressor.ReadUncompressedLength(&uncompressed_len)) { + return false; + } + + char c; + size_t allocated_size; + char* buf = uncompressed->GetAppendBufferVariable( + 1, uncompressed_len, &c, 1, &allocated_size); + + const size_t compressed_len = compressed->Available(); + // If we can get a flat buffer, then use it, otherwise do block by block + // uncompression + if (allocated_size >= uncompressed_len) { + SnappyArrayWriter writer(buf); + bool result = InternalUncompressAllTags(&decompressor, &writer, + compressed_len, uncompressed_len); + uncompressed->Append(buf, writer.Produced()); + return result; + } else { + SnappySinkAllocator allocator(uncompressed); + SnappyScatteredWriter writer(allocator); + return InternalUncompressAllTags(&decompressor, &writer, compressed_len, + uncompressed_len); + } +} + +} // end namespace snappy diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy.h new file mode 100644 index 0000000..4568db8 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy.h @@ -0,0 +1,203 @@ +// Copyright 2005 and onwards Google Inc. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// A light-weight compression algorithm. It is designed for speed of +// compression and decompression, rather than for the utmost in space +// savings. +// +// For getting better compression ratios when you are compressing data +// with long repeated sequences or compressing data that is similar to +// other data, while still compressing fast, you might look at first +// using BMDiff and then compressing the output of BMDiff with +// Snappy. + +#ifndef THIRD_PARTY_SNAPPY_SNAPPY_H__ +#define THIRD_PARTY_SNAPPY_SNAPPY_H__ + +#include +#include + +#include "snappy-stubs-public.h" + +namespace snappy { + class Source; + class Sink; + + // ------------------------------------------------------------------------ + // Generic compression/decompression routines. + // ------------------------------------------------------------------------ + + // Compress the bytes read from "*source" and append to "*sink". Return the + // number of bytes written. + size_t Compress(Source* source, Sink* sink); + + // Find the uncompressed length of the given stream, as given by the header. + // Note that the true length could deviate from this; the stream could e.g. + // be truncated. + // + // Also note that this leaves "*source" in a state that is unsuitable for + // further operations, such as RawUncompress(). You will need to rewind + // or recreate the source yourself before attempting any further calls. + bool GetUncompressedLength(Source* source, uint32* result); + + // ------------------------------------------------------------------------ + // Higher-level string based routines (should be sufficient for most users) + // ------------------------------------------------------------------------ + + // Sets "*output" to the compressed version of "input[0,input_length-1]". + // Original contents of *output are lost. + // + // REQUIRES: "input[]" is not an alias of "*output". + size_t Compress(const char* input, size_t input_length, string* output); + + // Decompresses "compressed[0,compressed_length-1]" to "*uncompressed". + // Original contents of "*uncompressed" are lost. + // + // REQUIRES: "compressed[]" is not an alias of "*uncompressed". + // + // returns false if the message is corrupted and could not be decompressed + bool Uncompress(const char* compressed, size_t compressed_length, + string* uncompressed); + + // Decompresses "compressed" to "*uncompressed". + // + // returns false if the message is corrupted and could not be decompressed + bool Uncompress(Source* compressed, Sink* uncompressed); + + // This routine uncompresses as much of the "compressed" as possible + // into sink. It returns the number of valid bytes added to sink + // (extra invalid bytes may have been added due to errors; the caller + // should ignore those). The emitted data typically has length + // GetUncompressedLength(), but may be shorter if an error is + // encountered. + size_t UncompressAsMuchAsPossible(Source* compressed, Sink* uncompressed); + + // ------------------------------------------------------------------------ + // Lower-level character array based routines. May be useful for + // efficiency reasons in certain circumstances. + // ------------------------------------------------------------------------ + + // REQUIRES: "compressed" must point to an area of memory that is at + // least "MaxCompressedLength(input_length)" bytes in length. + // + // Takes the data stored in "input[0..input_length]" and stores + // it in the array pointed to by "compressed". + // + // "*compressed_length" is set to the length of the compressed output. + // + // Example: + // char* output = new char[snappy::MaxCompressedLength(input_length)]; + // size_t output_length; + // RawCompress(input, input_length, output, &output_length); + // ... Process(output, output_length) ... + // delete [] output; + void RawCompress(const char* input, + size_t input_length, + char* compressed, + size_t* compressed_length); + + // Given data in "compressed[0..compressed_length-1]" generated by + // calling the Snappy::Compress routine, this routine + // stores the uncompressed data to + // uncompressed[0..GetUncompressedLength(compressed)-1] + // returns false if the message is corrupted and could not be decrypted + bool RawUncompress(const char* compressed, size_t compressed_length, + char* uncompressed); + + // Given data from the byte source 'compressed' generated by calling + // the Snappy::Compress routine, this routine stores the uncompressed + // data to + // uncompressed[0..GetUncompressedLength(compressed,compressed_length)-1] + // returns false if the message is corrupted and could not be decrypted + bool RawUncompress(Source* compressed, char* uncompressed); + + // Given data in "compressed[0..compressed_length-1]" generated by + // calling the Snappy::Compress routine, this routine + // stores the uncompressed data to the iovec "iov". The number of physical + // buffers in "iov" is given by iov_cnt and their cumulative size + // must be at least GetUncompressedLength(compressed). The individual buffers + // in "iov" must not overlap with each other. + // + // returns false if the message is corrupted and could not be decrypted + bool RawUncompressToIOVec(const char* compressed, size_t compressed_length, + const struct iovec* iov, size_t iov_cnt); + + // Given data from the byte source 'compressed' generated by calling + // the Snappy::Compress routine, this routine stores the uncompressed + // data to the iovec "iov". The number of physical + // buffers in "iov" is given by iov_cnt and their cumulative size + // must be at least GetUncompressedLength(compressed). The individual buffers + // in "iov" must not overlap with each other. + // + // returns false if the message is corrupted and could not be decrypted + bool RawUncompressToIOVec(Source* compressed, const struct iovec* iov, + size_t iov_cnt); + + // Returns the maximal size of the compressed representation of + // input data that is "source_bytes" bytes in length; + size_t MaxCompressedLength(size_t source_bytes); + + // REQUIRES: "compressed[]" was produced by RawCompress() or Compress() + // Returns true and stores the length of the uncompressed data in + // *result normally. Returns false on parsing error. + // This operation takes O(1) time. + bool GetUncompressedLength(const char* compressed, size_t compressed_length, + size_t* result); + + // Returns true iff the contents of "compressed[]" can be uncompressed + // successfully. Does not return the uncompressed data. Takes + // time proportional to compressed_length, but is usually at least + // a factor of four faster than actual decompression. + bool IsValidCompressedBuffer(const char* compressed, + size_t compressed_length); + + // Returns true iff the contents of "compressed" can be uncompressed + // successfully. Does not return the uncompressed data. Takes + // time proportional to *compressed length, but is usually at least + // a factor of four faster than actual decompression. + // On success, consumes all of *compressed. On failure, consumes an + // unspecified prefix of *compressed. + bool IsValidCompressed(Source* compressed); + + // The size of a compression block. Note that many parts of the compression + // code assumes that kBlockSize <= 65536; in particular, the hash table + // can only store 16-bit offsets, and EmitCopy() also assumes the offset + // is 65535 bytes or less. Note also that if you change this, it will + // affect the framing format (see framing_format.txt). + // + // Note that there might be older data around that is compressed with larger + // block sizes, so the decompression code should not rely on the + // non-existence of long backreferences. + static const int kBlockLog = 16; + static const size_t kBlockSize = 1 << kBlockLog; + + static const int kMaxHashTableBits = 14; + static const size_t kMaxHashTableSize = 1 << kMaxHashTableBits; +} // end namespace snappy + +#endif // THIRD_PARTY_SNAPPY_SNAPPY_H__ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy_unittest.cc b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy_unittest.cc new file mode 100644 index 0000000..fcb3261 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/snappy/snappy_unittest.cc @@ -0,0 +1,1410 @@ +// Copyright 2005 and onwards Google Inc. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include + + +#include +#include +#include +#include + +#include "snappy.h" +#include "snappy-internal.h" +#include "snappy-test.h" +#include "snappy-sinksource.h" + +DEFINE_int32(start_len, -1, + "Starting prefix size for testing (-1: just full file contents)"); +DEFINE_int32(end_len, -1, + "Starting prefix size for testing (-1: just full file contents)"); +DEFINE_int32(bytes, 10485760, + "How many bytes to compress/uncompress per file for timing"); + +DEFINE_bool(zlib, false, + "Run zlib compression (http://www.zlib.net)"); +DEFINE_bool(lzo, false, + "Run LZO compression (http://www.oberhumer.com/opensource/lzo/)"); +DEFINE_bool(snappy, true, "Run snappy compression"); + +DEFINE_bool(write_compressed, false, + "Write compressed versions of each file to .comp"); +DEFINE_bool(write_uncompressed, false, + "Write uncompressed versions of each file to .uncomp"); + +DEFINE_bool(snappy_dump_decompression_table, false, + "If true, we print the decompression table during tests."); + +namespace snappy { + +#if defined(HAVE_FUNC_MMAP) && defined(HAVE_FUNC_SYSCONF) + +// To test against code that reads beyond its input, this class copies a +// string to a newly allocated group of pages, the last of which +// is made unreadable via mprotect. Note that we need to allocate the +// memory with mmap(), as POSIX allows mprotect() only on memory allocated +// with mmap(), and some malloc/posix_memalign implementations expect to +// be able to read previously allocated memory while doing heap allocations. +class DataEndingAtUnreadablePage { + public: + explicit DataEndingAtUnreadablePage(const string& s) { + const size_t page_size = sysconf(_SC_PAGESIZE); + const size_t size = s.size(); + // Round up space for string to a multiple of page_size. + size_t space_for_string = (size + page_size - 1) & ~(page_size - 1); + alloc_size_ = space_for_string + page_size; + mem_ = mmap(NULL, alloc_size_, + PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + CHECK_NE(MAP_FAILED, mem_); + protected_page_ = reinterpret_cast(mem_) + space_for_string; + char* dst = protected_page_ - size; + memcpy(dst, s.data(), size); + data_ = dst; + size_ = size; + // Make guard page unreadable. + CHECK_EQ(0, mprotect(protected_page_, page_size, PROT_NONE)); + } + + ~DataEndingAtUnreadablePage() { + const size_t page_size = sysconf(_SC_PAGESIZE); + // Undo the mprotect. + CHECK_EQ(0, mprotect(protected_page_, page_size, PROT_READ|PROT_WRITE)); + CHECK_EQ(0, munmap(mem_, alloc_size_)); + } + + const char* data() const { return data_; } + size_t size() const { return size_; } + + private: + size_t alloc_size_; + void* mem_; + char* protected_page_; + const char* data_; + size_t size_; +}; + +#else // defined(HAVE_FUNC_MMAP) && defined(HAVE_FUNC_SYSCONF) + +// Fallback for systems without mmap. +typedef string DataEndingAtUnreadablePage; + +#endif + +enum CompressorType { + ZLIB, LZO, SNAPPY +}; + +const char* names[] = { + "ZLIB", "LZO", "SNAPPY" +}; + +static size_t MinimumRequiredOutputSpace(size_t input_size, + CompressorType comp) { + switch (comp) { +#ifdef ZLIB_VERSION + case ZLIB: + return ZLib::MinCompressbufSize(input_size); +#endif // ZLIB_VERSION + +#ifdef LZO_VERSION + case LZO: + return input_size + input_size/64 + 16 + 3; +#endif // LZO_VERSION + + case SNAPPY: + return snappy::MaxCompressedLength(input_size); + + default: + LOG(FATAL) << "Unknown compression type number " << comp; + return 0; + } +} + +// Returns true if we successfully compressed, false otherwise. +// +// If compressed_is_preallocated is set, do not resize the compressed buffer. +// This is typically what you want for a benchmark, in order to not spend +// time in the memory allocator. If you do set this flag, however, +// "compressed" must be preinitialized to at least MinCompressbufSize(comp) +// number of bytes, and may contain junk bytes at the end after return. +static bool Compress(const char* input, size_t input_size, CompressorType comp, + string* compressed, bool compressed_is_preallocated) { + if (!compressed_is_preallocated) { + compressed->resize(MinimumRequiredOutputSpace(input_size, comp)); + } + + switch (comp) { +#ifdef ZLIB_VERSION + case ZLIB: { + ZLib zlib; + uLongf destlen = compressed->size(); + int ret = zlib.Compress( + reinterpret_cast(string_as_array(compressed)), + &destlen, + reinterpret_cast(input), + input_size); + CHECK_EQ(Z_OK, ret); + if (!compressed_is_preallocated) { + compressed->resize(destlen); + } + return true; + } +#endif // ZLIB_VERSION + +#ifdef LZO_VERSION + case LZO: { + unsigned char* mem = new unsigned char[LZO1X_1_15_MEM_COMPRESS]; + lzo_uint destlen; + int ret = lzo1x_1_15_compress( + reinterpret_cast(input), + input_size, + reinterpret_cast(string_as_array(compressed)), + &destlen, + mem); + CHECK_EQ(LZO_E_OK, ret); + delete[] mem; + if (!compressed_is_preallocated) { + compressed->resize(destlen); + } + break; + } +#endif // LZO_VERSION + + case SNAPPY: { + size_t destlen; + snappy::RawCompress(input, input_size, + string_as_array(compressed), + &destlen); + CHECK_LE(destlen, snappy::MaxCompressedLength(input_size)); + if (!compressed_is_preallocated) { + compressed->resize(destlen); + } + break; + } + + default: { + return false; // the asked-for library wasn't compiled in + } + } + return true; +} + +static bool Uncompress(const string& compressed, CompressorType comp, + int size, string* output) { + switch (comp) { +#ifdef ZLIB_VERSION + case ZLIB: { + output->resize(size); + ZLib zlib; + uLongf destlen = output->size(); + int ret = zlib.Uncompress( + reinterpret_cast(string_as_array(output)), + &destlen, + reinterpret_cast(compressed.data()), + compressed.size()); + CHECK_EQ(Z_OK, ret); + CHECK_EQ(static_cast(size), destlen); + break; + } +#endif // ZLIB_VERSION + +#ifdef LZO_VERSION + case LZO: { + output->resize(size); + lzo_uint destlen; + int ret = lzo1x_decompress( + reinterpret_cast(compressed.data()), + compressed.size(), + reinterpret_cast(string_as_array(output)), + &destlen, + NULL); + CHECK_EQ(LZO_E_OK, ret); + CHECK_EQ(static_cast(size), destlen); + break; + } +#endif // LZO_VERSION + + case SNAPPY: { + snappy::RawUncompress(compressed.data(), compressed.size(), + string_as_array(output)); + break; + } + + default: { + return false; // the asked-for library wasn't compiled in + } + } + return true; +} + +static void Measure(const char* data, + size_t length, + CompressorType comp, + int repeats, + int block_size) { + // Run tests a few time and pick median running times + static const int kRuns = 5; + double ctime[kRuns]; + double utime[kRuns]; + int compressed_size = 0; + + { + // Chop the input into blocks + int num_blocks = (length + block_size - 1) / block_size; + std::vector input(num_blocks); + std::vector input_length(num_blocks); + std::vector compressed(num_blocks); + std::vector output(num_blocks); + for (int b = 0; b < num_blocks; b++) { + int input_start = b * block_size; + int input_limit = std::min((b+1)*block_size, length); + input[b] = data+input_start; + input_length[b] = input_limit-input_start; + + // Pre-grow the output buffer so we don't measure string append time. + compressed[b].resize(MinimumRequiredOutputSpace(block_size, comp)); + } + + // First, try one trial compression to make sure the code is compiled in + if (!Compress(input[0], input_length[0], comp, &compressed[0], true)) { + LOG(WARNING) << "Skipping " << names[comp] << ": " + << "library not compiled in"; + return; + } + + for (int run = 0; run < kRuns; run++) { + CycleTimer ctimer, utimer; + + for (int b = 0; b < num_blocks; b++) { + // Pre-grow the output buffer so we don't measure string append time. + compressed[b].resize(MinimumRequiredOutputSpace(block_size, comp)); + } + + ctimer.Start(); + for (int b = 0; b < num_blocks; b++) + for (int i = 0; i < repeats; i++) + Compress(input[b], input_length[b], comp, &compressed[b], true); + ctimer.Stop(); + + // Compress once more, with resizing, so we don't leave junk + // at the end that will confuse the decompressor. + for (int b = 0; b < num_blocks; b++) { + Compress(input[b], input_length[b], comp, &compressed[b], false); + } + + for (int b = 0; b < num_blocks; b++) { + output[b].resize(input_length[b]); + } + + utimer.Start(); + for (int i = 0; i < repeats; i++) + for (int b = 0; b < num_blocks; b++) + Uncompress(compressed[b], comp, input_length[b], &output[b]); + utimer.Stop(); + + ctime[run] = ctimer.Get(); + utime[run] = utimer.Get(); + } + + compressed_size = 0; + for (size_t i = 0; i < compressed.size(); i++) { + compressed_size += compressed[i].size(); + } + } + + std::sort(ctime, ctime + kRuns); + std::sort(utime, utime + kRuns); + const int med = kRuns/2; + + float comp_rate = (length / ctime[med]) * repeats / 1048576.0; + float uncomp_rate = (length / utime[med]) * repeats / 1048576.0; + string x = names[comp]; + x += ":"; + string urate = (uncomp_rate >= 0) + ? StringPrintf("%.1f", uncomp_rate) + : string("?"); + printf("%-7s [b %dM] bytes %6d -> %6d %4.1f%% " + "comp %5.1f MB/s uncomp %5s MB/s\n", + x.c_str(), + block_size/(1<<20), + static_cast(length), static_cast(compressed_size), + (compressed_size * 100.0) / std::max(1, length), + comp_rate, + urate.c_str()); +} + +static int VerifyString(const string& input) { + string compressed; + DataEndingAtUnreadablePage i(input); + const size_t written = snappy::Compress(i.data(), i.size(), &compressed); + CHECK_EQ(written, compressed.size()); + CHECK_LE(compressed.size(), + snappy::MaxCompressedLength(input.size())); + CHECK(snappy::IsValidCompressedBuffer(compressed.data(), compressed.size())); + + string uncompressed; + DataEndingAtUnreadablePage c(compressed); + CHECK(snappy::Uncompress(c.data(), c.size(), &uncompressed)); + CHECK_EQ(uncompressed, input); + return uncompressed.size(); +} + +static void VerifyStringSink(const string& input) { + string compressed; + DataEndingAtUnreadablePage i(input); + const size_t written = snappy::Compress(i.data(), i.size(), &compressed); + CHECK_EQ(written, compressed.size()); + CHECK_LE(compressed.size(), + snappy::MaxCompressedLength(input.size())); + CHECK(snappy::IsValidCompressedBuffer(compressed.data(), compressed.size())); + + string uncompressed; + uncompressed.resize(input.size()); + snappy::UncheckedByteArraySink sink(string_as_array(&uncompressed)); + DataEndingAtUnreadablePage c(compressed); + snappy::ByteArraySource source(c.data(), c.size()); + CHECK(snappy::Uncompress(&source, &sink)); + CHECK_EQ(uncompressed, input); +} + +static void VerifyIOVec(const string& input) { + string compressed; + DataEndingAtUnreadablePage i(input); + const size_t written = snappy::Compress(i.data(), i.size(), &compressed); + CHECK_EQ(written, compressed.size()); + CHECK_LE(compressed.size(), + snappy::MaxCompressedLength(input.size())); + CHECK(snappy::IsValidCompressedBuffer(compressed.data(), compressed.size())); + + // Try uncompressing into an iovec containing a random number of entries + // ranging from 1 to 10. + char* buf = new char[input.size()]; + ACMRandom rnd(input.size()); + size_t num = rnd.Next() % 10 + 1; + if (input.size() < num) { + num = input.size(); + } + struct iovec* iov = new iovec[num]; + int used_so_far = 0; + for (size_t i = 0; i < num; ++i) { + iov[i].iov_base = buf + used_so_far; + if (i == num - 1) { + iov[i].iov_len = input.size() - used_so_far; + } else { + // Randomly choose to insert a 0 byte entry. + if (rnd.OneIn(5)) { + iov[i].iov_len = 0; + } else { + iov[i].iov_len = rnd.Uniform(input.size()); + } + } + used_so_far += iov[i].iov_len; + } + CHECK(snappy::RawUncompressToIOVec( + compressed.data(), compressed.size(), iov, num)); + CHECK(!memcmp(buf, input.data(), input.size())); + delete[] iov; + delete[] buf; +} + +// Test that data compressed by a compressor that does not +// obey block sizes is uncompressed properly. +static void VerifyNonBlockedCompression(const string& input) { + if (input.length() > snappy::kBlockSize) { + // We cannot test larger blocks than the maximum block size, obviously. + return; + } + + string prefix; + Varint::Append32(&prefix, input.size()); + + // Setup compression table + snappy::internal::WorkingMemory wmem; + int table_size; + uint16* table = wmem.GetHashTable(input.size(), &table_size); + + // Compress entire input in one shot + string compressed; + compressed += prefix; + compressed.resize(prefix.size()+snappy::MaxCompressedLength(input.size())); + char* dest = string_as_array(&compressed) + prefix.size(); + char* end = snappy::internal::CompressFragment(input.data(), input.size(), + dest, table, table_size); + compressed.resize(end - compressed.data()); + + // Uncompress into string + string uncomp_str; + CHECK(snappy::Uncompress(compressed.data(), compressed.size(), &uncomp_str)); + CHECK_EQ(uncomp_str, input); + + // Uncompress using source/sink + string uncomp_str2; + uncomp_str2.resize(input.size()); + snappy::UncheckedByteArraySink sink(string_as_array(&uncomp_str2)); + snappy::ByteArraySource source(compressed.data(), compressed.size()); + CHECK(snappy::Uncompress(&source, &sink)); + CHECK_EQ(uncomp_str2, input); + + // Uncompress into iovec + { + static const int kNumBlocks = 10; + struct iovec vec[kNumBlocks]; + const int block_size = 1 + input.size() / kNumBlocks; + string iovec_data(block_size * kNumBlocks, 'x'); + for (int i = 0; i < kNumBlocks; i++) { + vec[i].iov_base = string_as_array(&iovec_data) + i * block_size; + vec[i].iov_len = block_size; + } + CHECK(snappy::RawUncompressToIOVec(compressed.data(), compressed.size(), + vec, kNumBlocks)); + CHECK_EQ(string(iovec_data.data(), input.size()), input); + } +} + +// Expand the input so that it is at least K times as big as block size +static string Expand(const string& input) { + static const int K = 3; + string data = input; + while (data.size() < K * snappy::kBlockSize) { + data += input; + } + return data; +} + +static int Verify(const string& input) { + VLOG(1) << "Verifying input of size " << input.size(); + + // Compress using string based routines + const int result = VerifyString(input); + + // Verify using sink based routines + VerifyStringSink(input); + + VerifyNonBlockedCompression(input); + VerifyIOVec(input); + if (!input.empty()) { + const string expanded = Expand(input); + VerifyNonBlockedCompression(expanded); + VerifyIOVec(input); + } + + return result; +} + + +static bool IsValidCompressedBuffer(const string& c) { + return snappy::IsValidCompressedBuffer(c.data(), c.size()); +} +static bool Uncompress(const string& c, string* u) { + return snappy::Uncompress(c.data(), c.size(), u); +} + +// This test checks to ensure that snappy doesn't coredump if it gets +// corrupted data. +TEST(CorruptedTest, VerifyCorrupted) { + string source = "making sure we don't crash with corrupted input"; + VLOG(1) << source; + string dest; + string uncmp; + snappy::Compress(source.data(), source.size(), &dest); + + // Mess around with the data. It's hard to simulate all possible + // corruptions; this is just one example ... + CHECK_GT(dest.size(), 3); + dest[1]--; + dest[3]++; + // this really ought to fail. + CHECK(!IsValidCompressedBuffer(dest)); + CHECK(!Uncompress(dest, &uncmp)); + + // This is testing for a security bug - a buffer that decompresses to 100k + // but we lie in the snappy header and only reserve 0 bytes of memory :) + source.resize(100000); + for (size_t i = 0; i < source.length(); ++i) { + source[i] = 'A'; + } + snappy::Compress(source.data(), source.size(), &dest); + dest[0] = dest[1] = dest[2] = dest[3] = 0; + CHECK(!IsValidCompressedBuffer(dest)); + CHECK(!Uncompress(dest, &uncmp)); + + if (sizeof(void *) == 4) { + // Another security check; check a crazy big length can't DoS us with an + // over-allocation. + // Currently this is done only for 32-bit builds. On 64-bit builds, + // where 3 GB might be an acceptable allocation size, Uncompress() + // attempts to decompress, and sometimes causes the test to run out of + // memory. + dest[0] = dest[1] = dest[2] = dest[3] = '\xff'; + // This decodes to a really large size, i.e., about 3 GB. + dest[4] = 'k'; + CHECK(!IsValidCompressedBuffer(dest)); + CHECK(!Uncompress(dest, &uncmp)); + } else { + LOG(WARNING) << "Crazy decompression lengths not checked on 64-bit build"; + } + + // This decodes to about 2 MB; much smaller, but should still fail. + dest[0] = dest[1] = dest[2] = '\xff'; + dest[3] = 0x00; + CHECK(!IsValidCompressedBuffer(dest)); + CHECK(!Uncompress(dest, &uncmp)); + + // try reading stuff in from a bad file. + for (int i = 1; i <= 3; ++i) { + string data = ReadTestDataFile(StringPrintf("baddata%d.snappy", i).c_str(), + 0); + string uncmp; + // check that we don't return a crazy length + size_t ulen; + CHECK(!snappy::GetUncompressedLength(data.data(), data.size(), &ulen) + || (ulen < (1<<20))); + uint32 ulen2; + snappy::ByteArraySource source(data.data(), data.size()); + CHECK(!snappy::GetUncompressedLength(&source, &ulen2) || + (ulen2 < (1<<20))); + CHECK(!IsValidCompressedBuffer(data)); + CHECK(!Uncompress(data, &uncmp)); + } +} + +// Helper routines to construct arbitrary compressed strings. +// These mirror the compression code in snappy.cc, but are copied +// here so that we can bypass some limitations in the how snappy.cc +// invokes these routines. +static void AppendLiteral(string* dst, const string& literal) { + if (literal.empty()) return; + int n = literal.size() - 1; + if (n < 60) { + // Fit length in tag byte + dst->push_back(0 | (n << 2)); + } else { + // Encode in upcoming bytes + char number[4]; + int count = 0; + while (n > 0) { + number[count++] = n & 0xff; + n >>= 8; + } + dst->push_back(0 | ((59+count) << 2)); + *dst += string(number, count); + } + *dst += literal; +} + +static void AppendCopy(string* dst, int offset, int length) { + while (length > 0) { + // Figure out how much to copy in one shot + int to_copy; + if (length >= 68) { + to_copy = 64; + } else if (length > 64) { + to_copy = 60; + } else { + to_copy = length; + } + length -= to_copy; + + if ((to_copy >= 4) && (to_copy < 12) && (offset < 2048)) { + assert(to_copy-4 < 8); // Must fit in 3 bits + dst->push_back(1 | ((to_copy-4) << 2) | ((offset >> 8) << 5)); + dst->push_back(offset & 0xff); + } else if (offset < 65536) { + dst->push_back(2 | ((to_copy-1) << 2)); + dst->push_back(offset & 0xff); + dst->push_back(offset >> 8); + } else { + dst->push_back(3 | ((to_copy-1) << 2)); + dst->push_back(offset & 0xff); + dst->push_back((offset >> 8) & 0xff); + dst->push_back((offset >> 16) & 0xff); + dst->push_back((offset >> 24) & 0xff); + } + } +} + +TEST(Snappy, SimpleTests) { + Verify(""); + Verify("a"); + Verify("ab"); + Verify("abc"); + + Verify("aaaaaaa" + string(16, 'b') + string("aaaaa") + "abc"); + Verify("aaaaaaa" + string(256, 'b') + string("aaaaa") + "abc"); + Verify("aaaaaaa" + string(2047, 'b') + string("aaaaa") + "abc"); + Verify("aaaaaaa" + string(65536, 'b') + string("aaaaa") + "abc"); + Verify("abcaaaaaaa" + string(65536, 'b') + string("aaaaa") + "abc"); +} + +// Verify max blowup (lots of four-byte copies) +TEST(Snappy, MaxBlowup) { + string input; + for (int i = 0; i < 20000; i++) { + ACMRandom rnd(i); + uint32 bytes = static_cast(rnd.Next()); + input.append(reinterpret_cast(&bytes), sizeof(bytes)); + } + for (int i = 19999; i >= 0; i--) { + ACMRandom rnd(i); + uint32 bytes = static_cast(rnd.Next()); + input.append(reinterpret_cast(&bytes), sizeof(bytes)); + } + Verify(input); +} + +TEST(Snappy, RandomData) { + ACMRandom rnd(FLAGS_test_random_seed); + + const int num_ops = 20000; + for (int i = 0; i < num_ops; i++) { + if ((i % 1000) == 0) { + VLOG(0) << "Random op " << i << " of " << num_ops; + } + + string x; + size_t len = rnd.Uniform(4096); + if (i < 100) { + len = 65536 + rnd.Uniform(65536); + } + while (x.size() < len) { + int run_len = 1; + if (rnd.OneIn(10)) { + run_len = rnd.Skewed(8); + } + char c = (i < 100) ? rnd.Uniform(256) : rnd.Skewed(3); + while (run_len-- > 0 && x.size() < len) { + x += c; + } + } + + Verify(x); + } +} + +TEST(Snappy, FourByteOffset) { + // The new compressor cannot generate four-byte offsets since + // it chops up the input into 32KB pieces. So we hand-emit the + // copy manually. + + // The two fragments that make up the input string. + string fragment1 = "012345689abcdefghijklmnopqrstuvwxyz"; + string fragment2 = "some other string"; + + // How many times each fragment is emitted. + const int n1 = 2; + const int n2 = 100000 / fragment2.size(); + const int length = n1 * fragment1.size() + n2 * fragment2.size(); + + string compressed; + Varint::Append32(&compressed, length); + + AppendLiteral(&compressed, fragment1); + string src = fragment1; + for (int i = 0; i < n2; i++) { + AppendLiteral(&compressed, fragment2); + src += fragment2; + } + AppendCopy(&compressed, src.size(), fragment1.size()); + src += fragment1; + CHECK_EQ(length, src.size()); + + string uncompressed; + CHECK(snappy::IsValidCompressedBuffer(compressed.data(), compressed.size())); + CHECK(snappy::Uncompress(compressed.data(), compressed.size(), + &uncompressed)); + CHECK_EQ(uncompressed, src); +} + +TEST(Snappy, IOVecEdgeCases) { + // Test some tricky edge cases in the iovec output that are not necessarily + // exercised by random tests. + + // Our output blocks look like this initially (the last iovec is bigger + // than depicted): + // [ ] [ ] [ ] [ ] [ ] + static const int kLengths[] = { 2, 1, 4, 8, 128 }; + + struct iovec iov[ARRAYSIZE(kLengths)]; + for (int i = 0; i < ARRAYSIZE(kLengths); ++i) { + iov[i].iov_base = new char[kLengths[i]]; + iov[i].iov_len = kLengths[i]; + } + + string compressed; + Varint::Append32(&compressed, 22); + + // A literal whose output crosses three blocks. + // [ab] [c] [123 ] [ ] [ ] + AppendLiteral(&compressed, "abc123"); + + // A copy whose output crosses two blocks (source and destination + // segments marked). + // [ab] [c] [1231] [23 ] [ ] + // ^--^ -- + AppendCopy(&compressed, 3, 3); + + // A copy where the input is, at first, in the block before the output: + // + // [ab] [c] [1231] [231231 ] [ ] + // ^--- ^--- + // Then during the copy, the pointers move such that the input and + // output pointers are in the same block: + // + // [ab] [c] [1231] [23123123] [ ] + // ^- ^- + // And then they move again, so that the output pointer is no longer + // in the same block as the input pointer: + // [ab] [c] [1231] [23123123] [123 ] + // ^-- ^-- + AppendCopy(&compressed, 6, 9); + + // Finally, a copy where the input is from several blocks back, + // and it also crosses three blocks: + // + // [ab] [c] [1231] [23123123] [123b ] + // ^ ^ + // [ab] [c] [1231] [23123123] [123bc ] + // ^ ^ + // [ab] [c] [1231] [23123123] [123bc12 ] + // ^- ^- + AppendCopy(&compressed, 17, 4); + + CHECK(snappy::RawUncompressToIOVec( + compressed.data(), compressed.size(), iov, ARRAYSIZE(iov))); + CHECK_EQ(0, memcmp(iov[0].iov_base, "ab", 2)); + CHECK_EQ(0, memcmp(iov[1].iov_base, "c", 1)); + CHECK_EQ(0, memcmp(iov[2].iov_base, "1231", 4)); + CHECK_EQ(0, memcmp(iov[3].iov_base, "23123123", 8)); + CHECK_EQ(0, memcmp(iov[4].iov_base, "123bc12", 7)); + + for (int i = 0; i < ARRAYSIZE(kLengths); ++i) { + delete[] reinterpret_cast(iov[i].iov_base); + } +} + +TEST(Snappy, IOVecLiteralOverflow) { + static const int kLengths[] = { 3, 4 }; + + struct iovec iov[ARRAYSIZE(kLengths)]; + for (int i = 0; i < ARRAYSIZE(kLengths); ++i) { + iov[i].iov_base = new char[kLengths[i]]; + iov[i].iov_len = kLengths[i]; + } + + string compressed; + Varint::Append32(&compressed, 8); + + AppendLiteral(&compressed, "12345678"); + + CHECK(!snappy::RawUncompressToIOVec( + compressed.data(), compressed.size(), iov, ARRAYSIZE(iov))); + + for (int i = 0; i < ARRAYSIZE(kLengths); ++i) { + delete[] reinterpret_cast(iov[i].iov_base); + } +} + +TEST(Snappy, IOVecCopyOverflow) { + static const int kLengths[] = { 3, 4 }; + + struct iovec iov[ARRAYSIZE(kLengths)]; + for (int i = 0; i < ARRAYSIZE(kLengths); ++i) { + iov[i].iov_base = new char[kLengths[i]]; + iov[i].iov_len = kLengths[i]; + } + + string compressed; + Varint::Append32(&compressed, 8); + + AppendLiteral(&compressed, "123"); + AppendCopy(&compressed, 3, 5); + + CHECK(!snappy::RawUncompressToIOVec( + compressed.data(), compressed.size(), iov, ARRAYSIZE(iov))); + + for (int i = 0; i < ARRAYSIZE(kLengths); ++i) { + delete[] reinterpret_cast(iov[i].iov_base); + } +} + +static bool CheckUncompressedLength(const string& compressed, + size_t* ulength) { + const bool result1 = snappy::GetUncompressedLength(compressed.data(), + compressed.size(), + ulength); + + snappy::ByteArraySource source(compressed.data(), compressed.size()); + uint32 length; + const bool result2 = snappy::GetUncompressedLength(&source, &length); + CHECK_EQ(result1, result2); + return result1; +} + +TEST(SnappyCorruption, TruncatedVarint) { + string compressed, uncompressed; + size_t ulength; + compressed.push_back('\xf0'); + CHECK(!CheckUncompressedLength(compressed, &ulength)); + CHECK(!snappy::IsValidCompressedBuffer(compressed.data(), compressed.size())); + CHECK(!snappy::Uncompress(compressed.data(), compressed.size(), + &uncompressed)); +} + +TEST(SnappyCorruption, UnterminatedVarint) { + string compressed, uncompressed; + size_t ulength; + compressed.push_back('\x80'); + compressed.push_back('\x80'); + compressed.push_back('\x80'); + compressed.push_back('\x80'); + compressed.push_back('\x80'); + compressed.push_back(10); + CHECK(!CheckUncompressedLength(compressed, &ulength)); + CHECK(!snappy::IsValidCompressedBuffer(compressed.data(), compressed.size())); + CHECK(!snappy::Uncompress(compressed.data(), compressed.size(), + &uncompressed)); +} + +TEST(SnappyCorruption, OverflowingVarint) { + string compressed, uncompressed; + size_t ulength; + compressed.push_back('\xfb'); + compressed.push_back('\xff'); + compressed.push_back('\xff'); + compressed.push_back('\xff'); + compressed.push_back('\x7f'); + CHECK(!CheckUncompressedLength(compressed, &ulength)); + CHECK(!snappy::IsValidCompressedBuffer(compressed.data(), compressed.size())); + CHECK(!snappy::Uncompress(compressed.data(), compressed.size(), + &uncompressed)); +} + +TEST(Snappy, ReadPastEndOfBuffer) { + // Check that we do not read past end of input + + // Make a compressed string that ends with a single-byte literal + string compressed; + Varint::Append32(&compressed, 1); + AppendLiteral(&compressed, "x"); + + string uncompressed; + DataEndingAtUnreadablePage c(compressed); + CHECK(snappy::Uncompress(c.data(), c.size(), &uncompressed)); + CHECK_EQ(uncompressed, string("x")); +} + +// Check for an infinite loop caused by a copy with offset==0 +TEST(Snappy, ZeroOffsetCopy) { + const char* compressed = "\x40\x12\x00\x00"; + // \x40 Length (must be > kMaxIncrementCopyOverflow) + // \x12\x00\x00 Copy with offset==0, length==5 + char uncompressed[100]; + EXPECT_FALSE(snappy::RawUncompress(compressed, 4, uncompressed)); +} + +TEST(Snappy, ZeroOffsetCopyValidation) { + const char* compressed = "\x05\x12\x00\x00"; + // \x05 Length + // \x12\x00\x00 Copy with offset==0, length==5 + EXPECT_FALSE(snappy::IsValidCompressedBuffer(compressed, 4)); +} + +namespace { + +int TestFindMatchLength(const char* s1, const char *s2, unsigned length) { + std::pair p = + snappy::internal::FindMatchLength(s1, s2, s2 + length); + CHECK_EQ(p.first < 8, p.second); + return p.first; +} + +} // namespace + +TEST(Snappy, FindMatchLength) { + // Exercise all different code paths through the function. + // 64-bit version: + + // Hit s1_limit in 64-bit loop, hit s1_limit in single-character loop. + EXPECT_EQ(6, TestFindMatchLength("012345", "012345", 6)); + EXPECT_EQ(11, TestFindMatchLength("01234567abc", "01234567abc", 11)); + + // Hit s1_limit in 64-bit loop, find a non-match in single-character loop. + EXPECT_EQ(9, TestFindMatchLength("01234567abc", "01234567axc", 9)); + + // Same, but edge cases. + EXPECT_EQ(11, TestFindMatchLength("01234567abc!", "01234567abc!", 11)); + EXPECT_EQ(11, TestFindMatchLength("01234567abc!", "01234567abc?", 11)); + + // Find non-match at once in first loop. + EXPECT_EQ(0, TestFindMatchLength("01234567xxxxxxxx", "?1234567xxxxxxxx", 16)); + EXPECT_EQ(1, TestFindMatchLength("01234567xxxxxxxx", "0?234567xxxxxxxx", 16)); + EXPECT_EQ(4, TestFindMatchLength("01234567xxxxxxxx", "01237654xxxxxxxx", 16)); + EXPECT_EQ(7, TestFindMatchLength("01234567xxxxxxxx", "0123456?xxxxxxxx", 16)); + + // Find non-match in first loop after one block. + EXPECT_EQ(8, TestFindMatchLength("abcdefgh01234567xxxxxxxx", + "abcdefgh?1234567xxxxxxxx", 24)); + EXPECT_EQ(9, TestFindMatchLength("abcdefgh01234567xxxxxxxx", + "abcdefgh0?234567xxxxxxxx", 24)); + EXPECT_EQ(12, TestFindMatchLength("abcdefgh01234567xxxxxxxx", + "abcdefgh01237654xxxxxxxx", 24)); + EXPECT_EQ(15, TestFindMatchLength("abcdefgh01234567xxxxxxxx", + "abcdefgh0123456?xxxxxxxx", 24)); + + // 32-bit version: + + // Short matches. + EXPECT_EQ(0, TestFindMatchLength("01234567", "?1234567", 8)); + EXPECT_EQ(1, TestFindMatchLength("01234567", "0?234567", 8)); + EXPECT_EQ(2, TestFindMatchLength("01234567", "01?34567", 8)); + EXPECT_EQ(3, TestFindMatchLength("01234567", "012?4567", 8)); + EXPECT_EQ(4, TestFindMatchLength("01234567", "0123?567", 8)); + EXPECT_EQ(5, TestFindMatchLength("01234567", "01234?67", 8)); + EXPECT_EQ(6, TestFindMatchLength("01234567", "012345?7", 8)); + EXPECT_EQ(7, TestFindMatchLength("01234567", "0123456?", 8)); + EXPECT_EQ(7, TestFindMatchLength("01234567", "0123456?", 7)); + EXPECT_EQ(7, TestFindMatchLength("01234567!", "0123456??", 7)); + + // Hit s1_limit in 32-bit loop, hit s1_limit in single-character loop. + EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd", "xxxxxxabcd", 10)); + EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd?", "xxxxxxabcd?", 10)); + EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcdef", "xxxxxxabcdef", 13)); + + // Same, but edge cases. + EXPECT_EQ(12, TestFindMatchLength("xxxxxx0123abc!", "xxxxxx0123abc!", 12)); + EXPECT_EQ(12, TestFindMatchLength("xxxxxx0123abc!", "xxxxxx0123abc?", 12)); + + // Hit s1_limit in 32-bit loop, find a non-match in single-character loop. + EXPECT_EQ(11, TestFindMatchLength("xxxxxx0123abc", "xxxxxx0123axc", 13)); + + // Find non-match at once in first loop. + EXPECT_EQ(6, TestFindMatchLength("xxxxxx0123xxxxxxxx", + "xxxxxx?123xxxxxxxx", 18)); + EXPECT_EQ(7, TestFindMatchLength("xxxxxx0123xxxxxxxx", + "xxxxxx0?23xxxxxxxx", 18)); + EXPECT_EQ(8, TestFindMatchLength("xxxxxx0123xxxxxxxx", + "xxxxxx0132xxxxxxxx", 18)); + EXPECT_EQ(9, TestFindMatchLength("xxxxxx0123xxxxxxxx", + "xxxxxx012?xxxxxxxx", 18)); + + // Same, but edge cases. + EXPECT_EQ(6, TestFindMatchLength("xxxxxx0123", "xxxxxx?123", 10)); + EXPECT_EQ(7, TestFindMatchLength("xxxxxx0123", "xxxxxx0?23", 10)); + EXPECT_EQ(8, TestFindMatchLength("xxxxxx0123", "xxxxxx0132", 10)); + EXPECT_EQ(9, TestFindMatchLength("xxxxxx0123", "xxxxxx012?", 10)); + + // Find non-match in first loop after one block. + EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd0123xx", + "xxxxxxabcd?123xx", 16)); + EXPECT_EQ(11, TestFindMatchLength("xxxxxxabcd0123xx", + "xxxxxxabcd0?23xx", 16)); + EXPECT_EQ(12, TestFindMatchLength("xxxxxxabcd0123xx", + "xxxxxxabcd0132xx", 16)); + EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcd0123xx", + "xxxxxxabcd012?xx", 16)); + + // Same, but edge cases. + EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd?123", 14)); + EXPECT_EQ(11, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd0?23", 14)); + EXPECT_EQ(12, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd0132", 14)); + EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd012?", 14)); +} + +TEST(Snappy, FindMatchLengthRandom) { + const int kNumTrials = 10000; + const int kTypicalLength = 10; + ACMRandom rnd(FLAGS_test_random_seed); + + for (int i = 0; i < kNumTrials; i++) { + string s, t; + char a = rnd.Rand8(); + char b = rnd.Rand8(); + while (!rnd.OneIn(kTypicalLength)) { + s.push_back(rnd.OneIn(2) ? a : b); + t.push_back(rnd.OneIn(2) ? a : b); + } + DataEndingAtUnreadablePage u(s); + DataEndingAtUnreadablePage v(t); + int matched = TestFindMatchLength(u.data(), v.data(), t.size()); + if (matched == t.size()) { + EXPECT_EQ(s, t); + } else { + EXPECT_NE(s[matched], t[matched]); + for (int j = 0; j < matched; j++) { + EXPECT_EQ(s[j], t[j]); + } + } + } +} + +static uint16 MakeEntry(unsigned int extra, + unsigned int len, + unsigned int copy_offset) { + // Check that all of the fields fit within the allocated space + assert(extra == (extra & 0x7)); // At most 3 bits + assert(copy_offset == (copy_offset & 0x7)); // At most 3 bits + assert(len == (len & 0x7f)); // At most 7 bits + return len | (copy_offset << 8) | (extra << 11); +} + +// Check that the decompression table is correct, and optionally print out +// the computed one. +TEST(Snappy, VerifyCharTable) { + using snappy::internal::LITERAL; + using snappy::internal::COPY_1_BYTE_OFFSET; + using snappy::internal::COPY_2_BYTE_OFFSET; + using snappy::internal::COPY_4_BYTE_OFFSET; + using snappy::internal::char_table; + + uint16 dst[256]; + + // Place invalid entries in all places to detect missing initialization + int assigned = 0; + for (int i = 0; i < 256; i++) { + dst[i] = 0xffff; + } + + // Small LITERAL entries. We store (len-1) in the top 6 bits. + for (unsigned int len = 1; len <= 60; len++) { + dst[LITERAL | ((len-1) << 2)] = MakeEntry(0, len, 0); + assigned++; + } + + // Large LITERAL entries. We use 60..63 in the high 6 bits to + // encode the number of bytes of length info that follow the opcode. + for (unsigned int extra_bytes = 1; extra_bytes <= 4; extra_bytes++) { + // We set the length field in the lookup table to 1 because extra + // bytes encode len-1. + dst[LITERAL | ((extra_bytes+59) << 2)] = MakeEntry(extra_bytes, 1, 0); + assigned++; + } + + // COPY_1_BYTE_OFFSET. + // + // The tag byte in the compressed data stores len-4 in 3 bits, and + // offset/256 in 5 bits. offset%256 is stored in the next byte. + // + // This format is used for length in range [4..11] and offset in + // range [0..2047] + for (unsigned int len = 4; len < 12; len++) { + for (unsigned int offset = 0; offset < 2048; offset += 256) { + dst[COPY_1_BYTE_OFFSET | ((len-4)<<2) | ((offset>>8)<<5)] = + MakeEntry(1, len, offset>>8); + assigned++; + } + } + + // COPY_2_BYTE_OFFSET. + // Tag contains len-1 in top 6 bits, and offset in next two bytes. + for (unsigned int len = 1; len <= 64; len++) { + dst[COPY_2_BYTE_OFFSET | ((len-1)<<2)] = MakeEntry(2, len, 0); + assigned++; + } + + // COPY_4_BYTE_OFFSET. + // Tag contents len-1 in top 6 bits, and offset in next four bytes. + for (unsigned int len = 1; len <= 64; len++) { + dst[COPY_4_BYTE_OFFSET | ((len-1)<<2)] = MakeEntry(4, len, 0); + assigned++; + } + + // Check that each entry was initialized exactly once. + EXPECT_EQ(256, assigned) << "Assigned only " << assigned << " of 256"; + for (int i = 0; i < 256; i++) { + EXPECT_NE(0xffff, dst[i]) << "Did not assign byte " << i; + } + + if (FLAGS_snappy_dump_decompression_table) { + printf("static const uint16 char_table[256] = {\n "); + for (int i = 0; i < 256; i++) { + printf("0x%04x%s", + dst[i], + ((i == 255) ? "\n" : (((i%8) == 7) ? ",\n " : ", "))); + } + printf("};\n"); + } + + // Check that computed table matched recorded table. + for (int i = 0; i < 256; i++) { + EXPECT_EQ(dst[i], char_table[i]) << "Mismatch in byte " << i; + } +} + +static void CompressFile(const char* fname) { + string fullinput; + CHECK_OK(file::GetContents(fname, &fullinput, file::Defaults())); + + string compressed; + Compress(fullinput.data(), fullinput.size(), SNAPPY, &compressed, false); + + CHECK_OK(file::SetContents(string(fname).append(".comp"), compressed, + file::Defaults())); +} + +static void UncompressFile(const char* fname) { + string fullinput; + CHECK_OK(file::GetContents(fname, &fullinput, file::Defaults())); + + size_t uncompLength; + CHECK(CheckUncompressedLength(fullinput, &uncompLength)); + + string uncompressed; + uncompressed.resize(uncompLength); + CHECK(snappy::Uncompress(fullinput.data(), fullinput.size(), &uncompressed)); + + CHECK_OK(file::SetContents(string(fname).append(".uncomp"), uncompressed, + file::Defaults())); +} + +static void MeasureFile(const char* fname) { + string fullinput; + CHECK_OK(file::GetContents(fname, &fullinput, file::Defaults())); + printf("%-40s :\n", fname); + + int start_len = (FLAGS_start_len < 0) ? fullinput.size() : FLAGS_start_len; + int end_len = fullinput.size(); + if (FLAGS_end_len >= 0) { + end_len = std::min(fullinput.size(), FLAGS_end_len); + } + for (int len = start_len; len <= end_len; len++) { + const char* const input = fullinput.data(); + int repeats = (FLAGS_bytes + len) / (len + 1); + if (FLAGS_zlib) Measure(input, len, ZLIB, repeats, 1024<<10); + if (FLAGS_lzo) Measure(input, len, LZO, repeats, 1024<<10); + if (FLAGS_snappy) Measure(input, len, SNAPPY, repeats, 4096<<10); + + // For block-size based measurements + if (0 && FLAGS_snappy) { + Measure(input, len, SNAPPY, repeats, 8<<10); + Measure(input, len, SNAPPY, repeats, 16<<10); + Measure(input, len, SNAPPY, repeats, 32<<10); + Measure(input, len, SNAPPY, repeats, 64<<10); + Measure(input, len, SNAPPY, repeats, 256<<10); + Measure(input, len, SNAPPY, repeats, 1024<<10); + } + } +} + +static struct { + const char* label; + const char* filename; + size_t size_limit; +} files[] = { + { "html", "html", 0 }, + { "urls", "urls.10K", 0 }, + { "jpg", "fireworks.jpeg", 0 }, + { "jpg_200", "fireworks.jpeg", 200 }, + { "pdf", "paper-100k.pdf", 0 }, + { "html4", "html_x_4", 0 }, + { "txt1", "alice29.txt", 0 }, + { "txt2", "asyoulik.txt", 0 }, + { "txt3", "lcet10.txt", 0 }, + { "txt4", "plrabn12.txt", 0 }, + { "pb", "geo.protodata", 0 }, + { "gaviota", "kppkn.gtb", 0 }, +}; + +static void BM_UFlat(int iters, int arg) { + StopBenchmarkTiming(); + + // Pick file to process based on "arg" + CHECK_GE(arg, 0); + CHECK_LT(arg, ARRAYSIZE(files)); + string contents = ReadTestDataFile(files[arg].filename, + files[arg].size_limit); + + string zcontents; + snappy::Compress(contents.data(), contents.size(), &zcontents); + char* dst = new char[contents.size()]; + + SetBenchmarkBytesProcessed(static_cast(iters) * + static_cast(contents.size())); + SetBenchmarkLabel(files[arg].label); + StartBenchmarkTiming(); + while (iters-- > 0) { + CHECK(snappy::RawUncompress(zcontents.data(), zcontents.size(), dst)); + } + StopBenchmarkTiming(); + + delete[] dst; +} +BENCHMARK(BM_UFlat)->DenseRange(0, ARRAYSIZE(files) - 1); + +static void BM_UValidate(int iters, int arg) { + StopBenchmarkTiming(); + + // Pick file to process based on "arg" + CHECK_GE(arg, 0); + CHECK_LT(arg, ARRAYSIZE(files)); + string contents = ReadTestDataFile(files[arg].filename, + files[arg].size_limit); + + string zcontents; + snappy::Compress(contents.data(), contents.size(), &zcontents); + + SetBenchmarkBytesProcessed(static_cast(iters) * + static_cast(contents.size())); + SetBenchmarkLabel(files[arg].label); + StartBenchmarkTiming(); + while (iters-- > 0) { + CHECK(snappy::IsValidCompressedBuffer(zcontents.data(), zcontents.size())); + } + StopBenchmarkTiming(); +} +BENCHMARK(BM_UValidate)->DenseRange(0, 4); + +static void BM_UIOVec(int iters, int arg) { + StopBenchmarkTiming(); + + // Pick file to process based on "arg" + CHECK_GE(arg, 0); + CHECK_LT(arg, ARRAYSIZE(files)); + string contents = ReadTestDataFile(files[arg].filename, + files[arg].size_limit); + + string zcontents; + snappy::Compress(contents.data(), contents.size(), &zcontents); + + // Uncompress into an iovec containing ten entries. + const int kNumEntries = 10; + struct iovec iov[kNumEntries]; + char *dst = new char[contents.size()]; + int used_so_far = 0; + for (int i = 0; i < kNumEntries; ++i) { + iov[i].iov_base = dst + used_so_far; + if (used_so_far == contents.size()) { + iov[i].iov_len = 0; + continue; + } + + if (i == kNumEntries - 1) { + iov[i].iov_len = contents.size() - used_so_far; + } else { + iov[i].iov_len = contents.size() / kNumEntries; + } + used_so_far += iov[i].iov_len; + } + + SetBenchmarkBytesProcessed(static_cast(iters) * + static_cast(contents.size())); + SetBenchmarkLabel(files[arg].label); + StartBenchmarkTiming(); + while (iters-- > 0) { + CHECK(snappy::RawUncompressToIOVec(zcontents.data(), zcontents.size(), iov, + kNumEntries)); + } + StopBenchmarkTiming(); + + delete[] dst; +} +BENCHMARK(BM_UIOVec)->DenseRange(0, 4); + +static void BM_UFlatSink(int iters, int arg) { + StopBenchmarkTiming(); + + // Pick file to process based on "arg" + CHECK_GE(arg, 0); + CHECK_LT(arg, ARRAYSIZE(files)); + string contents = ReadTestDataFile(files[arg].filename, + files[arg].size_limit); + + string zcontents; + snappy::Compress(contents.data(), contents.size(), &zcontents); + char* dst = new char[contents.size()]; + + SetBenchmarkBytesProcessed(static_cast(iters) * + static_cast(contents.size())); + SetBenchmarkLabel(files[arg].label); + StartBenchmarkTiming(); + while (iters-- > 0) { + snappy::ByteArraySource source(zcontents.data(), zcontents.size()); + snappy::UncheckedByteArraySink sink(dst); + CHECK(snappy::Uncompress(&source, &sink)); + } + StopBenchmarkTiming(); + + string s(dst, contents.size()); + CHECK_EQ(contents, s); + + delete[] dst; +} + +BENCHMARK(BM_UFlatSink)->DenseRange(0, ARRAYSIZE(files) - 1); + +static void BM_ZFlat(int iters, int arg) { + StopBenchmarkTiming(); + + // Pick file to process based on "arg" + CHECK_GE(arg, 0); + CHECK_LT(arg, ARRAYSIZE(files)); + string contents = ReadTestDataFile(files[arg].filename, + files[arg].size_limit); + + char* dst = new char[snappy::MaxCompressedLength(contents.size())]; + + SetBenchmarkBytesProcessed(static_cast(iters) * + static_cast(contents.size())); + StartBenchmarkTiming(); + + size_t zsize = 0; + while (iters-- > 0) { + snappy::RawCompress(contents.data(), contents.size(), dst, &zsize); + } + StopBenchmarkTiming(); + const double compression_ratio = + static_cast(zsize) / std::max(1, contents.size()); + SetBenchmarkLabel(StringPrintf("%s (%.2f %%)", + files[arg].label, 100.0 * compression_ratio)); + VLOG(0) << StringPrintf("compression for %s: %zd -> %zd bytes", + files[arg].label, contents.size(), zsize); + delete[] dst; +} +BENCHMARK(BM_ZFlat)->DenseRange(0, ARRAYSIZE(files) - 1); + +} // namespace snappy + +int main(int argc, char** argv) { + InitGoogle(argv[0], &argc, &argv, true); + RunSpecifiedBenchmarks(); + + if (argc >= 2) { + for (int arg = 1; arg < argc; arg++) { + if (FLAGS_write_compressed) { + snappy::CompressFile(argv[arg]); + } else if (FLAGS_write_uncompressed) { + snappy::UncompressFile(argv[arg]); + } else { + snappy::MeasureFile(argv[arg]); + } + } + return 0; + } + + return RUN_ALL_TESTS(); +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/solaris/config.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/solaris/config.h new file mode 100644 index 0000000..6cbbeb7 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/solaris/config.h @@ -0,0 +1,135 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Define to 1 if the compiler supports __builtin_ctz and friends. */ +#define HAVE_BUILTIN_CTZ 1 + +/* Define to 1 if the compiler supports __builtin_expect. */ +#define HAVE_BUILTIN_EXPECT 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_BYTESWAP_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Use the gflags package for command-line parsing. */ +/* #undef HAVE_GFLAGS */ + +/* Defined when Google Test is available. */ +/* #undef HAVE_GTEST */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `fastlz' library (-lfastlz). */ +/* #undef HAVE_LIBFASTLZ */ + +/* Define to 1 if you have the `lzf' library (-llzf). */ +/* #undef HAVE_LIBLZF */ + +/* Define to 1 if you have the `lzo2' library (-llzo2). */ +#define HAVE_LIBLZO2 1 + +/* Define to 1 if you have the `quicklz' library (-lquicklz). */ +/* #undef HAVE_LIBQUICKLZ */ + +/* Define to 1 if you have the `z' library (-lz). */ +#define HAVE_LIBZ 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_BYTESWAP_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_ENDIAN_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "snappy" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "snappy" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "snappy 1.1.4" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "snappy" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.1.4" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "1.1.4" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Define to `int' if does not define. */ +/* #undef ssize_t */ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/solaris/snappy-stubs-public.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/solaris/snappy-stubs-public.h new file mode 100644 index 0000000..3ce2b59 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/solaris/snappy-stubs-public.h @@ -0,0 +1,100 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: sesse@google.com (Steinar H. Gunderson) +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Various type stubs for the open-source version of Snappy. +// +// This file cannot include config.h, as it is included from snappy.h, +// which is a public header. Instead, snappy-stubs-public.h is generated by +// from snappy-stubs-public.h.in at configure time. + +#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ +#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ + +#if 1 +#include +#endif + +#if 1 +#include +#endif + +#if 1 +#include +#endif + +#define SNAPPY_MAJOR 1 +#define SNAPPY_MINOR 1 +#define SNAPPY_PATCHLEVEL 4 +#define SNAPPY_VERSION \ + ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL) + +#include + +namespace snappy { + +#if 1 +typedef int8_t int8; +typedef uint8_t uint8; +typedef int16_t int16; +typedef uint16_t uint16; +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#else +typedef signed char int8; +typedef unsigned char uint8; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +typedef long long int64; +typedef unsigned long long uint64; +#endif + +typedef std::string string; + +#ifndef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +#if !1 +// Windows does not have an iovec type, yet the concept is universally useful. +// It is simple to define it ourselves, so we put it inside our own namespace. +struct iovec { + void* iov_base; + size_t iov_len; +}; +#endif + +} // namespace snappy + +#endif // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/win32/config.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/win32/config.h new file mode 100644 index 0000000..bf444ec --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/win32/config.h @@ -0,0 +1,29 @@ +#include +typedef SSIZE_T ssize_t; + +/* Define to 1 if you have the header file. */ +#define HAVE_WINDOWS_H 1 + +/* Name of package */ +#define PACKAGE "snappy" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "snappy" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "snappy 1.1.4" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "snappy" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.1.4" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "1.1.4" diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/win32/snappy-stubs-public.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/win32/snappy-stubs-public.h new file mode 100644 index 0000000..4d64d46 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/deps/snappy/win32/snappy-stubs-public.h @@ -0,0 +1,100 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: sesse@google.com (Steinar H. Gunderson) +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Various type stubs for the open-source version of Snappy. +// +// This file cannot include config.h, as it is included from snappy.h, +// which is a public header. Instead, snappy-stubs-public.h is generated by +// from snappy-stubs-public.h.in at configure time. + +#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ +#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ + +#if 0 +#include +#endif + +#if 1 +#include +#endif + +#if 0 +#include +#endif + +#define SNAPPY_MAJOR 1 +#define SNAPPY_MINOR 1 +#define SNAPPY_PATCHLEVEL 4 +#define SNAPPY_VERSION \ + ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL) + +#include + +namespace snappy { + +#if 0 +typedef int8_t int8; +typedef uint8_t uint8; +typedef int16_t int16; +typedef uint16_t uint16; +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#else +typedef signed char int8; +typedef unsigned char uint8; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +typedef long long int64; +typedef unsigned long long uint64; +#endif + +typedef std::string string; + +#ifndef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +#if !0 +// Windows does not have an iovec type, yet the concept is universally useful. +// It is simple to define it ourselves, so we put it inside our own namespace. +struct iovec { + void* iov_base; + size_t iov_len; +}; +#endif + +} // namespace snappy + +#endif // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/index.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/index.d.ts new file mode 100644 index 0000000..4f3cfb0 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/index.d.ts @@ -0,0 +1,338 @@ +import { + AbstractLevel, + AbstractDatabaseOptions, + AbstractOpenOptions, + AbstractGetOptions, + AbstractGetManyOptions, + AbstractPutOptions, + AbstractDelOptions, + AbstractBatchOperation, + AbstractBatchOptions, + AbstractChainedBatch, + AbstractChainedBatchWriteOptions, + AbstractIteratorOptions, + AbstractIterator, + AbstractKeyIterator, + AbstractKeyIteratorOptions, + AbstractValueIterator, + AbstractValueIteratorOptions, + Transcoder, + NodeCallback +} from 'abstract-level' + +/** + * An {@link AbstractLevel} database backed by + * [LevelDB](https://github.com/google/leveldb). + * + * @template KDefault The default type of keys if not overridden on operations. + * @template VDefault The default type of values if not overridden on operations. + */ +declare class ClassicLevel + extends AbstractLevel { + /** + * Database constructor. + * + * @param location Directory path (relative or absolute) where LevelDB will + * store its files. + * @param options Options, of which some will be forwarded to {@link open}. + */ + constructor ( + location: string, + options?: DatabaseOptions | undefined + ) + + /** + * Location that was passed to the constructor. + */ + get location (): string + + open (): Promise + open (options: OpenOptions): Promise + open (callback: NodeCallback): void + open (options: OpenOptions, callback: NodeCallback): void + + get (key: KDefault): Promise + get (key: KDefault, callback: NodeCallback): void + get (key: K, options: GetOptions): Promise + get (key: K, options: GetOptions, callback: NodeCallback): void + + getMany (keys: KDefault[]): Promise + getMany (keys: KDefault[], callback: NodeCallback): void + getMany (keys: K[], options: GetManyOptions): Promise + getMany (keys: K[], options: GetManyOptions, callback: NodeCallback): void + + put (key: KDefault, value: VDefault): Promise + put (key: KDefault, value: VDefault, callback: NodeCallback): void + put (key: K, value: V, options: PutOptions): Promise + put (key: K, value: V, options: PutOptions, callback: NodeCallback): void + + del (key: KDefault): Promise + del (key: KDefault, callback: NodeCallback): void + del (key: K, options: DelOptions): Promise + del (key: K, options: DelOptions, callback: NodeCallback): void + + batch (operations: Array>): Promise + batch (operations: Array>, callback: NodeCallback): void + batch (operations: Array>, options: BatchOptions): Promise + batch (operations: Array>, options: BatchOptions, callback: NodeCallback): void + batch (): ChainedBatch + + iterator (): Iterator + iterator (options: IteratorOptions): Iterator + + keys (): KeyIterator + keys (options: KeyIteratorOptions): KeyIterator + + values (): ValueIterator + values (options: ValueIteratorOptions): ValueIterator + + /** + * Get the approximate number of bytes of file system space used by the range + * `[start..end)`. + */ + approximateSize (start: KDefault, end: KDefault): Promise + approximateSize (start: KDefault, end: KDefault, callback: NodeCallback): void + approximateSize (start: K, end: K, options: StartEndOptions): Promise + approximateSize (start: K, end: K, options: StartEndOptions, callback: NodeCallback): void + + /** + * Manually trigger a database compaction in the range `[start..end)`. + */ + compactRange (start: KDefault, end: KDefault): Promise + compactRange (start: KDefault, end: KDefault, callback: NodeCallback): void + compactRange (start: K, end: K, options: StartEndOptions): Promise + compactRange (start: K, end: K, options: StartEndOptions, callback: NodeCallback): void + + /** + * Get internal details from LevelDB. + */ + getProperty (property: string): string + + /** + * Completely remove an existing LevelDB database directory. Can be used in + * place of a full directory removal to only remove LevelDB-related files. + */ + static destroy (location: string): Promise + static destroy (location: string, callback: NodeCallback): void + + /** + * Attempt a restoration of a damaged database. Can also be used to perform + * a compaction of the LevelDB log into table files. + */ + static repair (location: string): Promise + static repair (location: string, callback: NodeCallback): void +} + +/** + * Options for the database constructor. + */ +export interface DatabaseOptions + extends AbstractDatabaseOptions, Omit {} + +/** + * Options for the {@link ClassicLevel.open} method. + */ +export interface OpenOptions extends AbstractOpenOptions { + /** + * Unless set to `false`, all _compressible_ data will be run through the + * Snappy compression algorithm before being stored. Snappy is very fast so + * leave this on unless you have good reason to turn it off. + * + * @defaultValue `true` + */ + compression?: boolean | undefined + + /** + * The size (in bytes) of the in-memory + * [LRU](http://en.wikipedia.org/wiki/Least_Recently_Used) + * cache with frequently used uncompressed block contents. + * + * @defaultValue `8 * 1024 * 1024` + */ + cacheSize?: number | undefined + + /** + * The maximum size (in bytes) of the log (in memory and stored in the `.log` + * file on disk). Beyond this size, LevelDB will convert the log data to the + * first level of sorted table files. From LevelDB documentation: + * + * > Larger values increase performance, especially during bulk loads. Up to + * > two write buffers may be held in memory at the same time, so you may + * > wish to adjust this parameter to control memory usage. Also, a larger + * > write buffer will result in a longer recovery time the next time the + * > database is opened. + * + * @defaultValue `4 * 1024 * 1024` + */ + writeBufferSize?: number | undefined + + /** + * The _approximate_ size of the blocks that make up the table files. The + * size relates to uncompressed data (hence "approximate"). Blocks are + * indexed in the table file and entry-lookups involve reading an entire + * block and parsing to discover the required entry. + * + * @defaultValue `4096` + */ + blockSize?: number | undefined + + /** + * The maximum number of files that LevelDB is allowed to have open at a + * time. If your database is likely to have a large working set, you may + * increase this value to prevent file descriptor churn. To calculate the + * number of files required for your working set, divide your total data size + * by `maxFileSize`. + * + * @defaultValue 1000 + */ + maxOpenFiles?: number | undefined + + /** + * The number of entries before restarting the "delta encoding" of keys + * within blocks. Each "restart" point stores the full key for the entry, + * between restarts, the common prefix of the keys for those entries is + * omitted. Restarts are similar to the concept of keyframes in video + * encoding and are used to minimise the amount of space required to store + * keys. This is particularly helpful when using deep namespacing / prefixing + * in your keys. + * + * @defaultValue `16` + */ + blockRestartInterval?: number | undefined + + /** + * The maximum amount of bytes to write to a file before switching to a new + * one. From LevelDB documentation: + * + * > If your filesystem is more efficient with larger files, you could + * > consider increasing the value. The downside will be longer compactions + * > and hence longer latency / performance hiccups. Another reason to + * > increase this parameter might be when you are initially populating a + * > large database. + * + * @defaultValue `2 * 1024 * 1024` + */ + maxFileSize?: number | undefined + + /** + * Allows multi-threaded access to a single DB instance for sharing a DB + * across multiple worker threads within the same process. + * + * @defaultValue `false` + */ + multithreading?: boolean | undefined +} + +/** + * Additional options for the {@link ClassicLevel.get} and {@link ClassicLevel.getMany} + * methods. + */ +declare interface ReadOptions { + /** + * Unless set to `false`, LevelDB will fill its in-memory + * [LRU](http://en.wikipedia.org/wiki/Least_Recently_Used) cache with data + * that was read. + * + * @defaultValue `true` + */ + fillCache?: boolean | undefined +} + +/** + * Options for the {@link ClassicLevel.get} method. + */ +export interface GetOptions extends AbstractGetOptions, ReadOptions {} + +/** + * Options for the {@link ClassicLevel.getMany} method. + */ +export interface GetManyOptions extends AbstractGetManyOptions, ReadOptions {} + +/** + * Additional options for the {@link ClassicLevel.iterator}, {@link ClassicLevel.keys} + * and {@link ClassicLevel.values} methods. + */ +export interface AdditionalIteratorOptions { + /** + * If set to `true`, LevelDB will fill its in-memory + * [LRU](http://en.wikipedia.org/wiki/Least_Recently_Used) cache with data + * that was read. + * + * @defaultValue `false` + */ + fillCache?: boolean | undefined + + /** + * Limit the amount of data that the iterator will hold in memory. + */ + highWaterMarkBytes?: number | undefined +} + +/** + * Additional options for the {@link ClassicLevel.put}, {@link ClassicLevel.del} + * and {@link ClassicLevel.batch} methods. + */ +declare interface WriteOptions { + /** + * If set to `true`, LevelDB will perform a synchronous write of the data + * although the operation will be asynchronous as far as Node.js or Electron + * is concerned. Normally, LevelDB passes the data to the operating system + * for writing and returns immediately. In contrast, a synchronous write will + * use [`fsync()`](https://man7.org/linux/man-pages/man2/fsync.2.html) or + * equivalent, so the operation will not complete until the data is actually + * on disk. Synchronous writes are significantly slower than asynchronous + * writes. + * + * @defaultValue `false` + */ + sync?: boolean | undefined +} + +/** + * Options for the {@link ClassicLevel.put} method. + */ +export interface PutOptions extends AbstractPutOptions, WriteOptions {} + +/** + * Options for the {@link ClassicLevel.del} method. + */ +export interface DelOptions extends AbstractDelOptions, WriteOptions {} + +/** + * Options for the {@link ClassicLevel.batch} method. + */ +export interface BatchOptions extends AbstractBatchOptions, WriteOptions {} + +/** + * Options for the {@link ChainedBatch.write} method. + */ +export interface ChainedBatchWriteOptions extends AbstractChainedBatchWriteOptions, WriteOptions {} + +export class ChainedBatch extends AbstractChainedBatch { + write (): Promise + write (options: ChainedBatchWriteOptions): Promise + write (callback: NodeCallback): void + write (options: ChainedBatchWriteOptions, callback: NodeCallback): void +} + +/** + * Options for the {@link ClassicLevel.approximateSize} and + * {@link ClassicLevel.compactRange} methods. + */ +export interface StartEndOptions { + /** + * Custom key encoding for this operation, used to encode `start` and `end`. + */ + keyEncoding?: string | Transcoder.PartialEncoder | undefined +} + +// Export remaining types so that consumers don't have to guess whether they're extended +export type BatchOperation = AbstractBatchOperation + +export type Iterator = AbstractIterator +export type KeyIterator = AbstractKeyIterator +export type ValueIterator = AbstractValueIterator + +export type IteratorOptions = AbstractIteratorOptions & AdditionalIteratorOptions +export type KeyIteratorOptions = AbstractKeyIteratorOptions & AdditionalIteratorOptions +export type ValueIteratorOptions = AbstractValueIteratorOptions & AdditionalIteratorOptions diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/index.js new file mode 100644 index 0000000..32a098d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/index.js @@ -0,0 +1,190 @@ +'use strict' + +const { AbstractLevel } = require('abstract-level') +const ModuleError = require('module-error') +const { fromCallback } = require('catering') +const fs = require('fs') +const binding = require('./binding') +const { ChainedBatch } = require('./chained-batch') +const { Iterator } = require('./iterator') + +const kPromise = Symbol('promise') +const kContext = Symbol('context') +const kLocation = Symbol('location') + +class ClassicLevel extends AbstractLevel { + constructor (location, options, _) { + // To help migrating to abstract-level + if (typeof options === 'function' || typeof _ === 'function') { + throw new ModuleError('The levelup-style callback argument has been removed', { + code: 'LEVEL_LEGACY' + }) + } + + if (typeof location !== 'string' || location === '') { + throw new TypeError("The first argument 'location' must be a non-empty string") + } + + super({ + encodings: { + buffer: true, + utf8: true, + view: true + }, + seek: true, + createIfMissing: true, + errorIfExists: true, + additionalMethods: { + approximateSize: true, + compactRange: true + } + }, options) + + this[kLocation] = location + this[kContext] = binding.db_init() + } + + get location () { + return this[kLocation] + } + + _open (options, callback) { + if (options.createIfMissing) { + fs.mkdir(this[kLocation], { recursive: true }, (err) => { + if (err) return callback(err) + binding.db_open(this[kContext], this[kLocation], options, callback) + }) + } else { + binding.db_open(this[kContext], this[kLocation], options, callback) + } + } + + _close (callback) { + binding.db_close(this[kContext], callback) + } + + _put (key, value, options, callback) { + binding.db_put(this[kContext], key, value, options, callback) + } + + _get (key, options, callback) { + binding.db_get(this[kContext], key, options, callback) + } + + _getMany (keys, options, callback) { + binding.db_get_many(this[kContext], keys, options, callback) + } + + _del (key, options, callback) { + binding.db_del(this[kContext], key, options, callback) + } + + _clear (options, callback) { + binding.db_clear(this[kContext], options, callback) + } + + _chainedBatch () { + return new ChainedBatch(this, this[kContext]) + } + + _batch (operations, options, callback) { + binding.batch_do(this[kContext], operations, options, callback) + } + + approximateSize (start, end, options, callback) { + if (arguments.length < 2 || typeof start === 'function' || typeof end === 'function') { + throw new TypeError("The arguments 'start' and 'end' are required") + } else if (typeof options === 'function') { + callback = options + options = null + } else if (typeof options !== 'object') { + options = null + } + + callback = fromCallback(callback, kPromise) + + if (this.status === 'opening') { + this.defer(() => this.approximateSize(start, end, options, callback)) + } else if (this.status !== 'open') { + this.nextTick(callback, new ModuleError('Database is not open: cannot call approximateSize()', { + code: 'LEVEL_DATABASE_NOT_OPEN' + })) + } else { + const keyEncoding = this.keyEncoding(options && options.keyEncoding) + start = keyEncoding.encode(start) + end = keyEncoding.encode(end) + binding.db_approximate_size(this[kContext], start, end, callback) + } + + return callback[kPromise] + } + + compactRange (start, end, options, callback) { + if (arguments.length < 2 || typeof start === 'function' || typeof end === 'function') { + throw new TypeError("The arguments 'start' and 'end' are required") + } else if (typeof options === 'function') { + callback = options + options = null + } else if (typeof options !== 'object') { + options = null + } + + callback = fromCallback(callback, kPromise) + + if (this.status === 'opening') { + this.defer(() => this.compactRange(start, end, options, callback)) + } else if (this.status !== 'open') { + this.nextTick(callback, new ModuleError('Database is not open: cannot call compactRange()', { + code: 'LEVEL_DATABASE_NOT_OPEN' + })) + } else { + const keyEncoding = this.keyEncoding(options && options.keyEncoding) + start = keyEncoding.encode(start) + end = keyEncoding.encode(end) + binding.db_compact_range(this[kContext], start, end, callback) + } + + return callback[kPromise] + } + + getProperty (property) { + if (typeof property !== 'string') { + throw new TypeError("The first argument 'property' must be a string") + } + + // Is synchronous, so can't be deferred + if (this.status !== 'open') { + throw new ModuleError('Database is not open', { + code: 'LEVEL_DATABASE_NOT_OPEN' + }) + } + + return binding.db_get_property(this[kContext], property) + } + + _iterator (options) { + return new Iterator(this, this[kContext], options) + } + + static destroy (location, callback) { + if (typeof location !== 'string' || location === '') { + throw new TypeError("The first argument 'location' must be a non-empty string") + } + + callback = fromCallback(callback, kPromise) + binding.destroy_db(location, callback) + return callback[kPromise] + } + + static repair (location, callback) { + if (typeof location !== 'string' || location === '') { + throw new TypeError("The first argument 'location' must be a non-empty string") + } + + callback = fromCallback(callback, kPromise) + binding.repair_db(location, callback) + return callback[kPromise] + } +} + +exports.ClassicLevel = ClassicLevel diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/iterator.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/iterator.js new file mode 100644 index 0000000..26212c7 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/iterator.js @@ -0,0 +1,106 @@ +'use strict' + +const { AbstractIterator } = require('abstract-level') +const binding = require('./binding') + +const kContext = Symbol('context') +const kCache = Symbol('cache') +const kFinished = Symbol('finished') +const kFirst = Symbol('first') +const kPosition = Symbol('position') +const kHandleNext = Symbol('handleNext') +const kHandleNextv = Symbol('handleNextv') +const kCallback = Symbol('callback') +const empty = [] + +// Does not implement _all() because the default implementation +// of abstract-level falls back to nextv(1000) and using all() +// on more entries than that probably isn't a realistic use case, +// so it'll typically just make one nextv(1000) call and there's +// no performance gain in overriding _all(). +class Iterator extends AbstractIterator { + constructor (db, context, options) { + super(db, options) + + this[kContext] = binding.iterator_init(context, options) + this[kHandleNext] = this[kHandleNext].bind(this) + this[kHandleNextv] = this[kHandleNextv].bind(this) + this[kCallback] = null + this[kFirst] = true + this[kCache] = empty + this[kFinished] = false + this[kPosition] = 0 + } + + _seek (target, options) { + this[kFirst] = true + this[kCache] = empty + this[kFinished] = false + this[kPosition] = 0 + + binding.iterator_seek(this[kContext], target) + } + + _next (callback) { + if (this[kPosition] < this[kCache].length) { + const entry = this[kCache][this[kPosition]++] + process.nextTick(callback, null, entry[0], entry[1]) + } else if (this[kFinished]) { + process.nextTick(callback) + } else { + this[kCallback] = callback + + if (this[kFirst]) { + // It's common to only want one entry initially or after a seek() + this[kFirst] = false + binding.iterator_nextv(this[kContext], 1, this[kHandleNext]) + } else { + // Limit the size of the cache to prevent starving the event loop + // while we're recursively calling process.nextTick(). + binding.iterator_nextv(this[kContext], 1000, this[kHandleNext]) + } + } + } + + [kHandleNext] (err, items, finished) { + const callback = this[kCallback] + if (err) return callback(err) + + this[kCache] = items + this[kFinished] = finished + this[kPosition] = 0 + + this._next(callback) + } + + _nextv (size, options, callback) { + if (this[kFinished]) { + process.nextTick(callback, null, []) + } else { + this[kCallback] = callback + this[kFirst] = false + binding.iterator_nextv(this[kContext], size, this[kHandleNextv]) + } + } + + [kHandleNextv] (err, items, finished) { + const callback = this[kCallback] + if (err) return callback(err) + this[kFinished] = finished + callback(null, items) + } + + _close (callback) { + this[kCache] = empty + this[kCallback] = null + + binding.iterator_close(this[kContext], callback) + } + + // Undocumented, exposed for tests only + get cached () { + return this[kCache].length - this[kPosition] + } +} + +exports.Iterator = Iterator diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/package.json new file mode 100644 index 0000000..b8df8ac --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/package.json @@ -0,0 +1,73 @@ +{ + "name": "classic-level", + "version": "1.4.1", + "description": "An abstract-level database backed by LevelDB", + "license": "MIT", + "main": "index.js", + "types": "./index.d.ts", + "scripts": { + "install": "node-gyp-build", + "test": "standard && (nyc -s tape test/*-test.js | faucet) && nyc report", + "test-gc": "node --expose-gc test/gc.js", + "test-electron": "electron test/electron.js", + "test-prebuild": "cross-env PREBUILDS_ONLY=1 npm t", + "coverage": "nyc report -r lcovonly", + "rebuild": "npm run install --build-from-source", + "prebuild": "prebuildify -t 8.14.0 --napi --strip", + "download-prebuilds": "prebuildify-ci download", + "hallmark": "hallmark --fix", + "dependency-check": "dependency-check --no-dev -i napi-macros . test/*.js", + "prepublishOnly": "npm run dependency-check", + "prebuild-linux-arm": "prebuildify-cross -i linux-armv6 -i linux-armv7 -i linux-arm64-lts -t 8.14.0 --napi --strip", + "prebuild-android-arm": "prebuildify-cross -i android-armv7 -i android-arm64 -t 8.14.0 --napi --strip", + "prebuild-linux-x64": "prebuildify-cross -i centos7-devtoolset7 -i alpine -t 8.14.0 --napi --strip", + "prebuild-darwin-x64+arm64": "prebuildify -t 8.14.0 --napi --strip --arch x64+arm64", + "prebuild-win32-x86": "prebuildify -t 8.14.0 --napi --strip", + "prebuild-win32-x64": "prebuildify -t 8.14.0 --napi --strip" + }, + "dependencies": { + "abstract-level": "^1.0.2", + "catering": "^2.1.0", + "module-error": "^1.0.1", + "napi-macros": "^2.2.2", + "node-gyp-build": "^4.3.0" + }, + "devDependencies": { + "@types/node": "^18.0.0", + "@voxpelli/tsconfig": "^4.0.0", + "async-each": "^1.0.3", + "cross-env": "^7.0.3", + "delayed": "^2.0.0", + "dependency-check": "^4.1.0", + "du": "^1.0.0", + "electron": "^21.0.1", + "faucet": "^0.0.3", + "glob": "^8.0.1", + "hallmark": "^4.1.0", + "mkfiletree": "^2.0.0", + "node-gyp": "^9.0.0", + "nyc": "^15.0.0", + "prebuildify": "^5.0.0", + "prebuildify-ci": "^1.0.4", + "prebuildify-cross": "^5.0.0", + "readfiletree": "^1.0.0", + "rimraf": "^3.0.0", + "standard": "^17.0.0", + "tape": "^5.5.0", + "tempy": "^1.0.1", + "typescript": "^4.5.5" + }, + "gypfile": true, + "repository": { + "type": "git", + "url": "https://github.com/Level/classic-level.git" + }, + "homepage": "https://github.com/Level/classic-level", + "keywords": [ + "leveldb", + "level" + ], + "engines": { + "node": ">=12" + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/prebuilds/win32-ia32/node.napi.node b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/prebuilds/win32-ia32/node.napi.node new file mode 100644 index 0000000..3c6b7e6 Binary files /dev/null and b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/prebuilds/win32-ia32/node.napi.node differ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/prebuilds/win32-x64/node.napi.node b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/prebuilds/win32-x64/node.napi.node new file mode 100644 index 0000000..ca5fd41 Binary files /dev/null and b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/classic-level/prebuilds/win32-x64/node.napi.node differ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/LICENSE new file mode 100644 index 0000000..5aac82c --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/LICENSE @@ -0,0 +1,11 @@ +Copyright 2008 Fair Oaks Labs, Inc. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/README.md new file mode 100644 index 0000000..cb7527b --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/README.md @@ -0,0 +1,51 @@ +# ieee754 [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url] + +[travis-image]: https://img.shields.io/travis/feross/ieee754/master.svg +[travis-url]: https://travis-ci.org/feross/ieee754 +[npm-image]: https://img.shields.io/npm/v/ieee754.svg +[npm-url]: https://npmjs.org/package/ieee754 +[downloads-image]: https://img.shields.io/npm/dm/ieee754.svg +[downloads-url]: https://npmjs.org/package/ieee754 +[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg +[standard-url]: https://standardjs.com + +[![saucelabs][saucelabs-image]][saucelabs-url] + +[saucelabs-image]: https://saucelabs.com/browser-matrix/ieee754.svg +[saucelabs-url]: https://saucelabs.com/u/ieee754 + +### Read/write IEEE754 floating point numbers from/to a Buffer or array-like object. + +## install + +``` +npm install ieee754 +``` + +## methods + +`var ieee754 = require('ieee754')` + +The `ieee754` object has the following functions: + +``` +ieee754.read = function (buffer, offset, isLE, mLen, nBytes) +ieee754.write = function (buffer, value, offset, isLE, mLen, nBytes) +``` + +The arguments mean the following: + +- buffer = the buffer +- offset = offset into the buffer +- value = value to set (only for `write`) +- isLe = is little endian? +- mLen = mantissa length +- nBytes = number of bytes + +## what is ieee754? + +The IEEE Standard for Floating-Point Arithmetic (IEEE 754) is a technical standard for floating-point computation. [Read more](http://en.wikipedia.org/wiki/IEEE_floating_point). + +## license + +BSD 3 Clause. Copyright (c) 2008, Fair Oaks Labs, Inc. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/index.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/index.d.ts new file mode 100644 index 0000000..f1e4354 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/index.d.ts @@ -0,0 +1,10 @@ +declare namespace ieee754 { + export function read( + buffer: Uint8Array, offset: number, isLE: boolean, mLen: number, + nBytes: number): number; + export function write( + buffer: Uint8Array, value: number, offset: number, isLE: boolean, + mLen: number, nBytes: number): void; + } + + export = ieee754; \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/index.js new file mode 100644 index 0000000..81d26c3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/index.js @@ -0,0 +1,85 @@ +/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ +exports.read = function (buffer, offset, isLE, mLen, nBytes) { + var e, m + var eLen = (nBytes * 8) - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var nBits = -7 + var i = isLE ? (nBytes - 1) : 0 + var d = isLE ? -1 : 1 + var s = buffer[offset + i] + + i += d + + e = s & ((1 << (-nBits)) - 1) + s >>= (-nBits) + nBits += eLen + for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} + + m = e & ((1 << (-nBits)) - 1) + e >>= (-nBits) + nBits += mLen + for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} + + if (e === 0) { + e = 1 - eBias + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity) + } else { + m = m + Math.pow(2, mLen) + e = e - eBias + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen) +} + +exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c + var eLen = (nBytes * 8) - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) + var i = isLE ? 0 : (nBytes - 1) + var d = isLE ? 1 : -1 + var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 + + value = Math.abs(value) + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0 + e = eMax + } else { + e = Math.floor(Math.log(value) / Math.LN2) + if (value * (c = Math.pow(2, -e)) < 1) { + e-- + c *= 2 + } + if (e + eBias >= 1) { + value += rt / c + } else { + value += rt * Math.pow(2, 1 - eBias) + } + if (value * c >= 2) { + e++ + c /= 2 + } + + if (e + eBias >= eMax) { + m = 0 + e = eMax + } else if (e + eBias >= 1) { + m = ((value * c) - 1) * Math.pow(2, mLen) + e = e + eBias + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) + e = 0 + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + + e = (e << mLen) | m + eLen += mLen + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + + buffer[offset + i - d] |= s * 128 +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/package.json new file mode 100644 index 0000000..7b23851 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/ieee754/package.json @@ -0,0 +1,52 @@ +{ + "name": "ieee754", + "description": "Read/write IEEE754 floating point numbers from/to a Buffer or array-like object", + "version": "1.2.1", + "author": { + "name": "Feross Aboukhadijeh", + "email": "feross@feross.org", + "url": "https://feross.org" + }, + "contributors": [ + "Romain Beauxis " + ], + "devDependencies": { + "airtap": "^3.0.0", + "standard": "*", + "tape": "^5.0.1" + }, + "keywords": [ + "IEEE 754", + "buffer", + "convert", + "floating point", + "ieee754" + ], + "license": "BSD-3-Clause", + "main": "index.js", + "types": "index.d.ts", + "repository": { + "type": "git", + "url": "git://github.com/feross/ieee754.git" + }, + "scripts": { + "test": "standard && npm run test-node && npm run test-browser", + "test-browser": "airtap -- test/*.js", + "test-browser-local": "airtap --local -- test/*.js", + "test-node": "tape test/*.js" + }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/LICENSE new file mode 100644 index 0000000..0c068ce --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Feross Aboukhadijeh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/README.md new file mode 100644 index 0000000..8c9785f --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/README.md @@ -0,0 +1,54 @@ +# is-buffer [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url] + +[travis-image]: https://img.shields.io/travis/feross/is-buffer/master.svg +[travis-url]: https://travis-ci.org/feross/is-buffer +[npm-image]: https://img.shields.io/npm/v/is-buffer.svg +[npm-url]: https://npmjs.org/package/is-buffer +[downloads-image]: https://img.shields.io/npm/dm/is-buffer.svg +[downloads-url]: https://npmjs.org/package/is-buffer +[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg +[standard-url]: https://standardjs.com + +#### Determine if an object is a [`Buffer`](http://nodejs.org/api/buffer.html) (including the [browserify Buffer](https://github.com/feross/buffer)) + +[![saucelabs][saucelabs-image]][saucelabs-url] + +[saucelabs-image]: https://saucelabs.com/browser-matrix/is-buffer.svg +[saucelabs-url]: https://saucelabs.com/u/is-buffer + +## Why not use `Buffer.isBuffer`? + +This module lets you check if an object is a `Buffer` without using `Buffer.isBuffer` (which includes the whole [buffer](https://github.com/feross/buffer) module in [browserify](http://browserify.org/)). + +It's future-proof and works in node too! + +## install + +```bash +npm install is-buffer +``` + +## usage + +```js +var isBuffer = require('is-buffer') + +isBuffer(new Buffer(4)) // true +isBuffer(Buffer.alloc(4)) //true + +isBuffer(undefined) // false +isBuffer(null) // false +isBuffer('') // false +isBuffer(true) // false +isBuffer(false) // false +isBuffer(0) // false +isBuffer(1) // false +isBuffer(1.0) // false +isBuffer('string') // false +isBuffer({}) // false +isBuffer(function foo () {}) // false +``` + +## license + +MIT. Copyright (C) [Feross Aboukhadijeh](http://feross.org). diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/index.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/index.d.ts new file mode 100644 index 0000000..7065c69 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/index.d.ts @@ -0,0 +1,2 @@ +declare function isBuffer(obj: any): boolean +export = isBuffer diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/index.js new file mode 100644 index 0000000..da9bfdd --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/index.js @@ -0,0 +1,11 @@ +/*! + * Determine if an object is a Buffer + * + * @author Feross Aboukhadijeh + * @license MIT + */ + +module.exports = function isBuffer (obj) { + return obj != null && obj.constructor != null && + typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/package.json new file mode 100644 index 0000000..7cd70d4 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/is-buffer/package.json @@ -0,0 +1,65 @@ +{ + "name": "is-buffer", + "description": "Determine if an object is a Buffer", + "version": "2.0.5", + "author": { + "name": "Feross Aboukhadijeh", + "email": "feross@feross.org", + "url": "https://feross.org" + }, + "bugs": { + "url": "https://github.com/feross/is-buffer/issues" + }, + "dependencies": {}, + "devDependencies": { + "airtap": "^3.0.0", + "standard": "*", + "tape": "^5.0.1" + }, + "engines": { + "node": ">=4" + }, + "keywords": [ + "arraybuffer", + "browser", + "browser buffer", + "browserify", + "buffer", + "buffers", + "core buffer", + "dataview", + "float32array", + "float64array", + "int16array", + "int32array", + "type", + "typed array", + "uint32array" + ], + "license": "MIT", + "main": "index.js", + "repository": { + "type": "git", + "url": "git://github.com/feross/is-buffer.git" + }, + "scripts": { + "test": "standard && npm run test-node && npm run test-browser", + "test-browser": "airtap -- test/*.js", + "test-browser-local": "airtap --local -- test/*.js", + "test-node": "tape test/*.js" + }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/CHANGELOG.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/CHANGELOG.md new file mode 100644 index 0000000..39e3478 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/CHANGELOG.md @@ -0,0 +1,95 @@ +# Changelog + +## [4.0.1] - 2022-03-06 + +### Fixed + +- Update support matrix ([`c30998f`](https://github.com/Level/supports/commit/c30998f)) (Vincent Weevers) +- Align LICENSE with SPDX ([`1bb9f9c`](https://github.com/Level/supports/commit/1bb9f9c)) (Vincent Weevers) + +## [4.0.0] - 2022-01-02 + +### Removed + +- **Breaking:** drop Node.js 10 ([`ad1c533`](https://github.com/Level/supports/commit/ad1c533)) (Vincent Weevers) +- Remove unused properties ([`c894eba`](https://github.com/Level/supports/commit/c894eba)) (Vincent Weevers). + +### Fixed + +- Align docs with `abstract-level` ([`c894eba`](https://github.com/Level/supports/commit/c894eba)) (Vincent Weevers). + +## [3.0.0] - 2021-11-07 + +_No longer compatible with `abstract-leveldown`, `levelup` or their dependents._ + +### Changed + +- **Breaking:** change `encodings` and `events` to always be objects ([`eb819a6`](https://github.com/Level/supports/commit/eb819a6)) (Vincent Weevers) +- **Breaking:** replace default export with named export ([`7e9e467`](https://github.com/Level/supports/commit/7e9e467)) (Vincent Weevers) + +### Added + +- Add typings ([`3df48fc`](https://github.com/Level/supports/commit/3df48fc)) (Vincent Weevers). + +## [2.1.0] - 2021-10-27 + +### Changed + +- Allow `manifest.encodings` to be an object ([`284e0db`](https://github.com/Level/supports/commit/284e0db)) (Vincent Weevers) + +## [2.0.2] - 2021-10-09 + +### Added + +- Add `idempotentOpen` and `passiveOpen` features ([`fc3f1e0`](https://github.com/Level/supports/commit/fc3f1e0)) (Vincent Weevers) +- Add `events` feature ([`22a3837`](https://github.com/Level/supports/commit/22a3837)) (Vincent Weevers) +- Document that `encodings` implies utf8 default ([`d1b6d89`](https://github.com/Level/supports/commit/d1b6d89)) (Vincent Weevers) +- Document `status` values ([`0837a16`](https://github.com/Level/supports/commit/0837a16)) (Vincent Weevers) + +### Fixed + +- Update support matrices ([`eb92d8b`](https://github.com/Level/supports/commit/eb92d8b), [`ef14920`](https://github.com/Level/supports/commit/ef14920), [`0681a1e`](https://github.com/Level/supports/commit/0681a1e), [`ca2a0e6`](https://github.com/Level/supports/commit/ca2a0e6)) (Vincent Weevers) + +## [2.0.1] - 2021-09-24 + +### Added + +- Add features: `getMany`, `keyIterator`, `valueIterator`, `iteratorNextv`, `iteratorAll` ([#11](https://github.com/Level/supports/issues/11)) ([`b44a410`](https://github.com/Level/supports/commit/b44a410)) (Vincent Weevers) + +## [2.0.0] - 2021-04-09 + +### Changed + +- **Breaking:** modernize syntax and bump standard ([`5d7cc6d`](https://github.com/Level/supports/commit/5d7cc6d)) ([Level/community#98](https://github.com/Level/community/issues/98)) (Vincent Weevers). Drops support of node 6, node 8 and old browsers. + +## [1.0.1] - 2019-10-13 + +### Added + +- Document format of `additionalMethods` ([`192bc9e`](https://github.com/Level/supports/commit/192bc9e)) ([**@vweevers**](https://github.com/vweevers)) + +### Fixed + +- Clone `additionalMethods` to prevent mutation ([#4](https://github.com/Level/supports/issues/4)) ([**@vweevers**](https://github.com/vweevers)) + +## [1.0.0] - 2019-09-22 + +:seedling: Initial release. + +[4.0.1]: https://github.com/Level/supports/releases/tag/v4.0.1 + +[4.0.0]: https://github.com/Level/supports/releases/tag/v4.0.0 + +[3.0.0]: https://github.com/Level/supports/releases/tag/v3.0.0 + +[2.1.0]: https://github.com/Level/supports/releases/tag/v2.1.0 + +[2.0.2]: https://github.com/Level/supports/releases/tag/v2.0.2 + +[2.0.1]: https://github.com/Level/supports/releases/tag/v2.0.1 + +[2.0.0]: https://github.com/Level/supports/releases/tag/v2.0.0 + +[1.0.1]: https://github.com/Level/supports/releases/tag/v1.0.1 + +[1.0.0]: https://github.com/Level/supports/releases/tag/v1.0.0 diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/LICENSE new file mode 100644 index 0000000..363e094 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 The contributors to level-supports. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/README.md new file mode 100644 index 0000000..47cf97f --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/README.md @@ -0,0 +1,405 @@ +# level-supports + +**Create a manifest describing the abilities of an [`abstract-level`](https://github.com/Level/abstract-level) database.** No longer compatible with [`levelup`](https://github.com/Level/levelup) or [`abstract-leveldown`](https://github.com/Level/abstract-leveldown) since version 3.0.0. + +[![level badge][level-badge]](https://github.com/Level/awesome) +[![npm](https://img.shields.io/npm/v/level-supports.svg)](https://www.npmjs.com/package/level-supports) +[![Node version](https://img.shields.io/node/v/level-supports.svg)](https://www.npmjs.com/package/level-supports) +[![Test](https://img.shields.io/github/workflow/status/Level/supports/Test?label=test)](https://github.com/Level/supports/actions/workflows/test.yml) +[![Coverage](https://img.shields.io/codecov/c/github/Level/supports?label=\&logo=codecov\&logoColor=fff)](https://codecov.io/gh/Level/supports) +[![Standard](https://img.shields.io/badge/standard-informational?logo=javascript\&logoColor=fff)](https://standardjs.com) +[![Common Changelog](https://common-changelog.org/badge.svg)](https://common-changelog.org) +[![Donate](https://img.shields.io/badge/donate-orange?logo=open-collective\&logoColor=fff)](https://opencollective.com/level) + +## Usage + +```js +const { supports } = require('level-supports') + +db.supports = supports({ + permanence: false, + encodings: { + utf8: true + } +}) +``` + +Receivers of the db can then use it like so: + +```js +if (!db.supports.permanence) { + throw new Error('Persistent storage is required') +} +``` + +## API + +### `manifest = supports([manifest, ..])` + +Given zero or more manifest objects, returns a merged and enriched manifest object that has truthy properties for each of the features listed below. + +For future extensibility, the properties are truthy rather than strictly typed booleans. Falsy or absent properties are converted to `false`, other values are allowed: + +```js +supports().snapshots // false +supports({ snapshots: true }).snapshots // true +supports({ snapshots: {} }).snapshots // {} +supports({ snapshots: 1 }, { snapshots: 2 }).snapshots // 2 +``` + +For consumers of the manifest this means they should check support like so: + +```js +if (db.supports.snapshots) +``` + +Rather than: + +```js +if (db.supports.snapshots === true) +``` + +**Note:** the manifest describes high-level features that typically encompass multiple methods of a db. It is currently not a goal to describe a full API, or versions of it. + +## Features + +### `snapshots` (boolean) + +Does the database have snapshot guarantees? Meaning that reads are unaffected by simultaneous writes. For example, an iterator should read from a snapshot of the database, created at the time `db.iterator()` was called. This means the iterator will not see the data of simultaneous write operations. + +Must be `false` if any of the following is true: + +- Reads don't operate on a snapshot +- Snapshots are created asynchronously. + +
+Support matrix + +| Module | Snapshot guarantee | +| :------------------- | :-------------------------- | +| `classic-level` | ✅ | +| `memory-level` | ✅ | +| `browser-level` | ❌ | +| `rocks-level` | ✅ | +| `leveldown` | ✅ | +| `rocksdb` | ✅ | +| `memdown` | ✅ | +| `level-js` | ✅ (by buffering) | +| `encoding-down` | ✅ | +| `deferred-leveldown` | ✅ | +| `levelup` | ✅ | +| `level-packager` | ✅ | +| `level` | ✅ | +| `level-mem` | ✅ | +| `level-rocksdb` | ✅ | +| `subleveldown` | ✅ | +| `multileveldown` | ✅ (unless `retry` is true) | +| `level-party` | ❌ (unless `retry` is false) | + +
+ +### `permanence` (boolean) + +Does data survive after process (or environment) exit? Typically true. False for [`memory-level`](https://github.com/Level/memory-level) and [`memdown`](https://github.com/Level/memdown). + +### `seek` (boolean) + +Do iterators support [`seek(..)`](https://github.com/Level/abstract-level/#iteratorseektarget-options)? + +
+Support matrix + +| Module | Support | +| :------------------- | :------ | +| `abstract-level` | ✅ 1.0.0 | +| `classic-level` | ✅ 1.0.0 | +| `memory-level` | ✅ 1.0.0 | +| `browser-level` | ✅ 1.0.0 | +| `rocks-level` | ✅ 1.0.0 | +| `abstract-leveldown` | ✅ 6.0.0 | +| `leveldown` | ✅ 1.2.0 | +| `rocksdb` | ✅ 1.0.0 | +| `memdown` | ✅ 4.1.0 | +| `level-js` | ❌ | +| `encoding-down` | ✅ 6.1.0 | +| `deferred-leveldown` | ✅ 5.1.0 | +| `levelup` | ✅ n/a | +| `level-packager` | ✅ n/a | +| `level` | ✅ 8.0.0 | +| `level-mem` | ✅ 4.0.0 | +| `level-rocksdb` | ✅ 1.0.0 | +| `subleveldown` | ✅ 4.1.0 | +| `multileveldown` | ❌ | +| `level-party` | ❌ | + +
+ +#### `clear` (boolean) + +Does the database support `db.clear()`? Always true since `abstract-level@1`. + +
+Support matrix + +See also [Level/community#79](https://github.com/Level/community/issues/79). + +| Module | Support | Optimized | +| :------------------------------ | :------ | :-------- | +| `abstract-level` and dependents | ✅ 1.0.0 | ✅ 1.0.0 | +| `abstract-leveldown` | ✅ 6.1.0 | n/a | +| `leveldown` | ✅ 5.2.0 | ✅ 6.0.3 | +| `rocksdb` | ✅ 4.1.0 | ✅ 5.2.0 | +| `memdown` | ✅ 5.0.0 | ✅ 6.1.1 | +| `level-js` | ✅ 5.0.0 | ✅ 5.0.0 | +| `encoding-down` | ✅ 6.2.0 | n/a | +| `deferred-leveldown` | ✅ 5.2.0 | n/a | +| `levelup` | ✅ 4.2.0 | n/a | +| `level-packager` | ✅ 5.0.3 | n/a | +| `level` | ✅ 6.0.0 | ✅ 7.0.1 | +| `level-mem` | ✅ 5.0.1 | ✅ 6.0.1 | +| `level-rocksdb` | ✅ 5.0.0 | ✅ 5.0.0 | +| `subleveldown` | ✅ 4.2.1 | ✅ 4.2.1 | +| `multileveldown` | ✅ 5.0.0 | ✅ 5.0.0 | +| `level-party` | ✅ 5.1.0 | ✅ 5.1.0 | + +
+ +### `status` (boolean) + +Does the database have a [`status`](https://github.com/Level/abstract-level/#dbstatus) property? Always true since `abstract-level@1`. + +### `deferredOpen` (boolean) + +Can operations like `db.put()` be called without explicitly opening the db? Like so: + +```js +const db = new Level() +await db.put('key', 'value') +``` + +Always true since `abstract-level@1`. + +### `createIfMissing`, `errorIfExists` (boolean) + +Does `db.open()` support these options? + +
+Support matrix + +| Module | Support | +| :-------------- | :------ | +| `classic-level` | ✅ | +| `rocks-level` | ✅ | +| `memory-level` | ❌ | +| `browser-level` | ❌ | +| `leveldown` | ✅ | +| `rocksdb` | ✅ | +| `memdown` | ❌ | +| `level-js` | ❌ | + +
+ +### `promises` (boolean) + +Do all database methods (that don't otherwise have a return value) support promises, in addition to callbacks? Such that, when a callback argument is omitted, a promise is returned: + +```js +db.put('key', 'value', callback) +await db.put('key', 'value') +``` + +Always true since `abstract-level@1`. + +
+Support matrix + +| Module | Support | +| :---------------------------------- | :------------------- | +| `abstract-level` and dependents | ✅ | +| `abstract-leveldown` and dependents | ❌ (except iterators) | +| `levelup` | ✅ | +| `level-packager` | ✅ | +| `level` | ✅ | +| `level-mem` | ✅ | +| `level-rocksdb` | ✅ | +| `subleveldown` | ✅ | +| `multileveldown` | ✅ | +| `level-party` | ✅ | + +
+ +### `events` (object) + +Which events does the database emit, as indicated by nested properties? For example: + +```js +if (db.supports.events.put) { + db.on('put', listener) +} +``` + +### `streams` (boolean) + +Does database have the methods `createReadStream`, `createKeyStream` and `createValueStream`, following the API documented in `levelup`? For `abstract-level` databases, a standalone module called [`level-read-stream`](https://github.com/Level/read-stream) is available. + +
+Support matrix + +| Module | Support | +| :---------------------------------- | :------ | +| `abstract-level` and dependents | ❌ | +| `abstract-leveldown` and dependents | ❌ | +| `levelup` | ✅ | +| `level-packager` | ✅ | +| `level` | ✅ | +| `level-mem` | ✅ | +| `level-rocksdb` | ✅ | +| `subleveldown` | ✅ | +| `multileveldown` | ✅ | +| `level-party` | ✅ | + +
+ +### `encodings` (object) + +Which encodings (by name) does the database support, as indicated by nested properties? For example: + +```js +{ utf8: true, json: true } +``` + +As the `encodings` property cannot be false (anymore, since `level-supports` v3.0.0) it implies that the database supports `keyEncoding` and `valueEncoding` options on all relevant methods, uses a default encoding of utf8 and that hence all of its read operations return strings rather than buffers by default. + +
+Support matrix (general support) + +_This matrix just indicates general support of encodings as a feature, not that the listed modules support the `encodings` property exactly as described above, which only works on `abstract-level` databases._ + +| Module | Support | +| :------------------------------------- | :------ | +| `abstract-level` (and dependents) | ✅ | +| `abstract-leveldown` (and dependents) | ❌ | +| `encoding-down` | ✅ | +| `levelup` | ✅ | +| `level-packager` | ✅ | +| `level` | ✅ | +| `level-mem` | ✅ | +| `level-rocksdb` | ✅ | +| `subleveldown` | ✅ | +| `multileveldown` | ✅ | +| `level-party` | ✅ | + +
+ +
+Support matrix (specific encodings) + +_This matrix lists which encodings are supported as indicated by e.g. `db.supports.encodings.utf8`. Encodings that encode to another (like how `'json'` encodes to `'utf8'`) are excluded here, though they are present in `db.supports.encodings`._ + +| Module | `'utf8'` | `'buffer'` | `'view'` | +| :-------------- | :------------ | :------------ | :------------ | +| `classic-level` | ✅ | ✅ | ✅ 1 | +| `memory-level` | ✅ 2 | ✅ 2 | ✅ 2 | +| `browser-level` | ✅ 1 | ✅ 1 | ✅ | +| `rocks-level` | ✅ | ✅ | ✅ 1 | +| `level@8` | ✅ 3 | ✅ 3 | ✅ 3 | + + + +1. Transcoded (which may have a performance impact). +2. Can be controlled via `storeEncoding` option. +3. Whether it's transcoded depends on environment (Node.js or browser). + + + +
+ +### `getMany` (boolean) + +Does the database support `db.getMany()`? Always true since `abstract-level@1`. + +
+Support matrix + +| Module | Support | +| :------------------------------ | :------ | +| `abstract-level` and dependents | ✅ 1.0.0 | +| `abstract-leveldown` | ✅ 7.2.0 | +| `leveldown` | ✅ 6.1.0 | +| `rocksdb` | ✅ 5.2.0 | +| `memdown` | ✅ | +| `level-js` | ✅ 6.1.0 | +| `encoding-down` | ✅ 7.1.0 | +| `deferred-leveldown` | ✅ 7.0.0 | +| `levelup` | ✅ 5.1.0 | +| `level` | ✅ 7.0.1 | +| `level-mem` | ✅ 6.0.1 | +| `level-rocksdb` | ✅ 5.0.0 | +| `subleveldown` | ✅ 6.0.0 | +| `multileveldown` | ✅ 5.0.0 | +| `level-party` | ✅ 5.1.0 | + +
+ +### `keyIterator` (boolean) + +Does the database have a `keys([options])` method that returns a key iterator? Always true since `abstract-level@1`. + +### `valueIterator` (boolean) + +Does the database have a `values([options])` method that returns a key iterator? Always true since `abstract-level@1`. + +### `iteratorNextv` (boolean) + +Do iterators have a `nextv(size[, options][, callback])` method? Always true since `abstract-level@1`. + +### `iteratorAll` (boolean) + +Do iterators have a `all([options][, callback])` method? Always true since `abstract-level@1`. + +### `additionalMethods` (object) + +Declares support of additional methods, that are not part of the `abstract-level` interface. In the form of: + +```js +{ + foo: true, + bar: true +} +``` + +Which says the db has two methods, `foo` and `bar`. It might be used like so: + +```js +if (db.supports.additionalMethods.foo) { + db.foo() +} +``` + +For future extensibility, the properties of `additionalMethods` should be taken as truthy rather than strictly typed booleans. We may add additional metadata (see [#1](https://github.com/Level/supports/issues/1)). + +## Install + +With [npm](https://npmjs.org) do: + +``` +npm install level-supports +``` + +## Contributing + +[`Level/supports`](https://github.com/Level/supports) is an **OPEN Open Source Project**. This means that: + +> Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project. + +See the [Contribution Guide](https://github.com/Level/community/blob/master/CONTRIBUTING.md) for more details. + +## Donate + +Support us with a monthly donation on [Open Collective](https://opencollective.com/level) and help us continue our work. + +## License + +[MIT](LICENSE) + +[level-badge]: https://leveljs.org/img/badge.svg diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/index.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/index.d.ts new file mode 100644 index 0000000..905b248 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/index.d.ts @@ -0,0 +1,161 @@ +/** + * Given zero or more manifest objects, returns a merged and enriched manifest object + * that has truthy properties for each of the features listed in {@link IManifest}. + * + * @param manifests Partial manifest(s) to merge. + */ +export function supports (...manifests: Array>): IManifest + +/** + * Describes the abilities of an + * [`abstract-level`](https://github.com/Level/abstract-level) database. Support matrices + * for known `abstract-level` implementations can be found in + * [`level-supports`](https://github.com/Level/supports#features). + */ +export interface IManifest { + /** + * Does the database have snapshot guarantees? Meaning that reads are unaffected by + * simultaneous writes. For example, an iterator should read from a snapshot of the + * database, created at the time `db.iterator()` was called. This means the iterator + * will not see the data of simultaneous write operations. + */ + snapshots: boolean + + /** + * Does data survive after process (or environment) exit? + */ + permanence: boolean + + /** + * Do iterators support + * [`seek(..)`](https://github.com/Level/abstract-level/#iteratorseektarget-options)? + */ + seek: boolean + + /** + * Does the database support `db.clear()`? Always true since `abstract-level@1`. + */ + clear: boolean + + /** + * Does the database support `db.getMany()`? Always true since `abstract-level@1`. + */ + getMany: boolean + + /** + * Does the database have a `keys([options])` method that returns a key iterator? + * Always true since `abstract-level@1`. + */ + keyIterator: boolean + + /** + * Does the database have a `values([options])` method that returns a key iterator? + * Always true since `abstract-level@1`. + */ + valueIterator: boolean + + /** + * Do iterators have a `nextv(size[, options][, callback])` method? Always true since + * `abstract-level@1`. + */ + iteratorNextv: boolean + + /** + * Do iterators have a `all([options][, callback])` method? Always true since + * `abstract-level@1`. + */ + iteratorAll: boolean + + /** + * Does the database have a + * [`status`](https://github.com/Level/abstract-level/#dbstatus) property? Always true + * since `abstract-level@1`. + */ + status: boolean + + /** + * Does `db.open()` and the database constructor support this option? + */ + createIfMissing: boolean + + /** + * Does `db.open()` and the database constructor support this option? + */ + errorIfExists: boolean + + /** + * Can operations like `db.put()` be called without explicitly opening the db? Like so: + * + * ```js + * const db = new Level() + * await db.put('key', 'value') + * ``` + * + * Always true since `abstract-level@1`. + */ + deferredOpen: boolean + + /** + * Do all database methods (that don't otherwise have a return value) support promises, + * in addition to callbacks? Such that, when a callback argument is omitted, a promise + * is returned: + * + * ```js + * db.put('key', 'value', callback) + * await db.put('key', 'value') + * ``` + * + * Always true since `abstract-level@1`. + */ + promises: boolean + + /** + * Does database have the methods `createReadStream`, `createKeyStream` and + * `createValueStream`, following the API documented in `levelup`? For `abstract-level` + * databases, a standalone module called + * [`level-read-stream`](https://github.com/Level/read-stream) is available. + */ + streams: boolean + + /** + * Which encodings (by name) does the database support, as indicated by nested + * properties? For example: + * + * ```js + * { utf8: true, json: true } + * ``` + */ + encodings: Record + + /** + * Which events does the database emit, as indicated by nested properties? For example: + * + * ```js + * if (db.supports.events.put) { + * db.on('put', listener) + * } + * ``` + */ + events: Record + + /** + * Declares support of additional methods, that are not part of the `abstract-level` + * interface. In the form of: + * + * ```js + * { + * foo: true, + * bar: true + * } + * ``` + * + * Which says the db has two methods, `foo` and `bar`. It might be used like so: + * + * ```js + * if (db.supports.additionalMethods.foo) { + * db.foo() + * } + * ``` + */ + additionalMethods: Record +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/index.js new file mode 100644 index 0000000..cb0369a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/index.js @@ -0,0 +1,26 @@ +'use strict' + +exports.supports = function supports (...manifests) { + const manifest = manifests.reduce((acc, m) => Object.assign(acc, m), {}) + + return Object.assign(manifest, { + snapshots: manifest.snapshots || false, + permanence: manifest.permanence || false, + seek: manifest.seek || false, + clear: manifest.clear || false, + getMany: manifest.getMany || false, + keyIterator: manifest.keyIterator || false, + valueIterator: manifest.valueIterator || false, + iteratorNextv: manifest.iteratorNextv || false, + iteratorAll: manifest.iteratorAll || false, + status: manifest.status || false, + createIfMissing: manifest.createIfMissing || false, + errorIfExists: manifest.errorIfExists || false, + deferredOpen: manifest.deferredOpen || false, + promises: manifest.promises || false, + streams: manifest.streams || false, + encodings: Object.assign({}, manifest.encodings), + events: Object.assign({}, manifest.events), + additionalMethods: Object.assign({}, manifest.additionalMethods) + }) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/package.json new file mode 100644 index 0000000..9bdbeda --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/package.json @@ -0,0 +1,48 @@ +{ + "name": "level-supports", + "version": "4.0.1", + "description": "Create a manifest describing the abilities of an abstract-level database", + "license": "MIT", + "scripts": { + "test": "standard && ts-standard *.ts && hallmark && (nyc -s node test/self.js | faucet) && nyc report", + "test-browsers-local": "airtap --coverage test/self.js && nyc report", + "coverage": "nyc report -r lcovonly", + "hallmark": "hallmark --fix" + }, + "types": "./index.d.ts", + "files": [ + "test", + "CHANGELOG.md", + "index.js", + "index.d.ts" + ], + "devDependencies": { + "@types/node": "^17.0.6", + "@voxpelli/tsconfig": "^3.1.0", + "airtap": "^4.0.4", + "airtap-playwright": "^1.0.1", + "faucet": "^0.0.1", + "hallmark": "^4.0.0", + "nyc": "^15.1.0", + "standard": "^16.0.3", + "tape": "^5.4.0", + "ts-standard": "^11.0.0", + "typescript": "^4.5.4" + }, + "repository": { + "type": "git", + "url": "https://github.com/Level/supports.git" + }, + "homepage": "https://github.com/Level/supports", + "keywords": [ + "abstract-leveldown", + "database", + "db", + "level", + "levelup", + "manifest" + ], + "engines": { + "node": ">=12" + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/test/cloneable.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/test/cloneable.js new file mode 100644 index 0000000..768d1bf --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/test/cloneable.js @@ -0,0 +1,26 @@ +'use strict' + +const { supports } = require('..') + +// Every object in a manifest must have a unique identity, to avoid accidental +// mutation. In supports() we only shallowly clone the manifest object itself +// and additionalMethods. If in the future we add more objects to manifests, +// this test will break and we'll know to start performing a deep clone. +module.exports = function cloneable (t, manifest) { + const copy = supports(manifest) + verifyUnique(t, 'manifest', manifest, copy) +} + +function verifyUnique (t, path, a, b) { + if (isObject(a) && isObject(b)) { + t.ok(a !== b, path + ' has unique identity') + + Object.keys(a).forEach(function (key) { + verifyUnique(t, path + '.' + key, a[key], b[key]) + }) + } +} + +function isObject (o) { + return typeof o === 'object' && o !== null +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/test/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/test/index.js new file mode 100644 index 0000000..0208416 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/test/index.js @@ -0,0 +1,29 @@ +'use strict' + +const shape = require('./shape') +const cloneable = require('./cloneable') + +module.exports = function suite (test, testCommon) { + test('db has manifest', function (t) { + const db = testCommon.factory() + const manifest = db.supports + + shape(t, manifest) + cloneable(t, manifest) + + const before = Object.assign({}, manifest, { + additionalMethods: Object.assign({}, manifest.additionalMethods) + }) + + db.open(function (err) { + t.ifError(err, 'no open error') + t.same(db.supports, before, 'manifest did not change after open') + + db.close(function (err) { + t.ifError(err, 'no close error') + t.same(db.supports, before, 'manifest did not change after close') + t.end() + }) + }) + }) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/test/self.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/test/self.js new file mode 100644 index 0000000..2382a30 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/test/self.js @@ -0,0 +1,75 @@ +'use strict' + +const test = require('tape') +const { supports } = require('..') +const shape = require('./shape') +const cloneable = require('./cloneable') + +test('no options', function (t) { + shape(t, supports()) + cloneable(t, supports()) + t.end() +}) + +test('falsy options', function (t) { + ;[null, false, undefined, 0, ''].forEach(function (value) { + const manifest = supports({ + seek: value, + additionalMethods: { + foo: value + } + }) + + shape(t, manifest) + t.is(manifest.seek, false) + }) + + t.end() +}) + +test('truthy options', function (t) { + ;[true, {}, 'yes', 1, []].forEach(function (value) { + const manifest = supports({ + streams: value, + additionalMethods: { + foo: value + } + }) + + shape(t, manifest) + t.same(manifest.streams, value) + t.same(manifest.additionalMethods.foo, value) + }) + + t.end() +}) + +test('merges input objects without mutating them', function (t) { + const input1 = { seek: null, streams: false } + const input2 = { streams: true, additionalMethods: {} } + const manifest = supports(input1, input2) + + manifest.foobar = true + manifest.additionalMethods.baz = true + + t.same(input1, { seek: null, streams: false }) + t.same(input2, { streams: true, additionalMethods: {} }) + t.is(manifest.seek, false) + t.is(manifest.streams, true) + shape(t, manifest) + t.end() +}) + +test('inherits additionalMethods', function (t) { + const manifest = supports({ additionalMethods: { foo: true } }, {}) + t.same(manifest.additionalMethods, { foo: true }) + t.end() +}) + +test('does not merge additionalMethods', function (t) { + const input1 = { additionalMethods: { foo: true } } + const input2 = { additionalMethods: { bar: true } } + const manifest = supports(input1, input2) + t.same(manifest.additionalMethods, { bar: true }) + t.end() +}) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/test/shape.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/test/shape.js new file mode 100644 index 0000000..82198ec --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-supports/test/shape.js @@ -0,0 +1,22 @@ +'use strict' + +const hasOwnProperty = Object.prototype.hasOwnProperty + +module.exports = function shape (t, manifest) { + t.ok(isObject(manifest), 'manifest is object') + t.ok(isObject(manifest.additionalMethods), 'additionalMethods is object') + + for (const k in manifest) { + if (!hasOwnProperty.call(manifest, k)) continue + + if (manifest[k]) { + t.ok(manifest[k], 'truthy: ' + k) + } else { + t.is(manifest[k], false, 'false: ' + k) + } + } +} + +function isObject (o) { + return typeof o === 'object' && o !== null +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/CHANGELOG.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/CHANGELOG.md new file mode 100644 index 0000000..875ae74 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog + +## [1.0.1] - 2021-11-26 + +### Fixed + +- Remove `codecs` from supported interfaces in `README` ([`3b6b003`](https://github.com/Level/transcoder/commit/3b6b003)) + +## [1.0.0] - 2021-11-07 + +_:seedling: Initial release._ + +[1.0.1]: https://github.com/Level/transcoder/releases/tag/v1.0.1 + +[1.0.0]: https://github.com/Level/transcoder/releases/tag/v1.0.0 diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/LICENSE new file mode 100644 index 0000000..cdc0612 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2012 The contributors to level-transcoder and level-codec. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/README.md new file mode 100644 index 0000000..6c1931a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/README.md @@ -0,0 +1,214 @@ +# level-transcoder + +**Encode data with built-in or custom encodings.** The successor to [`level-codec`][level-codec] that transcodes encodings from and to internal data formats supported by a database. This allows a database to store data in a format of its choice (Buffer, Uint8Array or String) with zero-effort support of known encodings. That includes other encoding interfaces in the ecosystem like [`abstract-encoding`][abstract-enc] and [`multiformats`][blockcodec]. + +[![level badge][level-badge]](https://github.com/Level/awesome) +[![npm](https://img.shields.io/npm/v/level-transcoder.svg)](https://www.npmjs.com/package/level-transcoder) +[![Node version](https://img.shields.io/node/v/level-transcoder.svg)](https://www.npmjs.com/package/level-transcoder) +[![Test](https://img.shields.io/github/workflow/status/Level/transcoder/Test?label=test)](https://github.com/Level/transcoder/actions/workflows/test.yml) +[![Coverage](https://img.shields.io/codecov/c/github/Level/transcoder?label=\&logo=codecov\&logoColor=fff)](https://codecov.io/gh/Level/transcoder) +[![Standard](https://img.shields.io/badge/standard-informational?logo=javascript\&logoColor=fff)](https://standardjs.com) +[![Common Changelog](https://common-changelog.org/badge.svg)](https://common-changelog.org) +[![Donate](https://img.shields.io/badge/donate-orange?logo=open-collective\&logoColor=fff)](https://opencollective.com/level) + +## Usage + +Create a transcoder, passing a desired format: + +```js +const { Transcoder } = require('level-transcoder') + +const transcoder1 = new Transcoder(['view']) +const transcoder2 = new Transcoder(['buffer']) +const transcoder3 = new Transcoder(['utf8']) +``` + +Then select an encoding and encode some data: + +```js +// Uint8Array(3) [ 49, 50, 51 ] +console.log(transcoder1.encoding('json').encode(123)) + +// +console.log(transcoder2.encoding('json').encode(123)) + +// '123' +console.log(transcoder3.encoding('json').encode(123)) +``` + +If the `Transcoder` constructor is given multiple formats then `Transcoder#encoding()` selects an encoding with the best fitting format. Consider a database like [`leveldown`][leveldown] which has the ability to return data as a Buffer or string. If an `encoding.decode(data)` function needs a string, we'll want to fetch that `data` from the database as a string. This avoids the cost of having to convert a Buffer to a string. So we'd use the following transcoder: + +```js +const transcoder = new Transcoder(['buffer', 'utf8']) +``` + +Then, knowing for example that the return value of `JSON.stringify(data)` is a UTF-8 string which matches one of the given formats, the `'json'` encoding will return a string here: + +```js +// '123' +console.log(transcoder.encoding('json').encode(123)) +``` + +In contrast, data encoded as a `'view'` (for now that just means Uint8Array) would get transcoded into the `'buffer'` encoding. Copying of data is avoided where possible, like how the underlying ArrayBuffer of a view can be passed to `Buffer.from(..)` without a copy. + +Lastly, encodings returned by `Transcoder#encoding()` have a `format` property to be used to forward information to an underlying store. For example: an input value of `{ x: 3 }` using the `'json'` encoding which has a `format` of `'utf8'`, can be forwarded as value `'{"x":3}'` with encoding `'utf8'`. Vice versa for output. + +## Encodings + +### Built-in Encodings + +These encodings can be used out of the box and are to be selected by name. + +In this table, the _input_ is what `encode()` accepts. The _format_ is what `encode()` returns as well as what `decode()` accepts. The _output_ is what `decode()` returns. The TypeScript typings of `level-transcoder` have generic type parameters with matching names: `TIn`, `TFormat` and `TOut`. + +| Name | Input | Format | Output | +| :---------------------- | :------------------------- | :--------- | :-------------- | +| `'buffer'` 1 | Buffer, Uint8Array, String | `'buffer'` | Buffer | +| `'view'` | Uint8Array, Buffer, String | `'view'` | Uint8Array | +| `'utf8'` | String, Buffer, Uint8Array | `'utf8'` | String | +| `'json'` | Any JSON type | `'utf8'` | As input | +| `'hex'` | String (hex), Buffer | `'buffer'` | String (hex) | +| `'base64'` | String (base64), Buffer | `'buffer'` | String (base64) | + +1 Aliased as `'binary'`. Use of this alias does not affect the ability to transcode. + +### Transcoder Encodings + +It's not necessary to use or reference the below encodings directly. They're listed here for implementation notes and to show how input and output is the same; it's the format that differs. + +Custom encodings are transcoded in the same way and require no additional setup. For example: if a custom encoding has `{ name: 'example', format: 'utf8' }` then `level-transcoder` will create transcoder encodings on demand with names `'example+buffer'` and `'example+view'`. + +| Name | Input | Format | Output | +| :--------------------------- | :------------------------- | :--------- | :-------------- | +| `'buffer+view'` | Buffer, Uint8Array, String | `'view'` | Buffer | +| `'view+buffer'` | Uint8Array, Buffer, String | `'buffer'` | Uint8Array | +| `'utf8+view'` | String, Buffer, Uint8Array | `'view'` | String | +| `'utf8+buffer'` | String, Buffer, Uint8Array | `'buffer'` | String | +| `'json+view'` | Any JSON type | `'view'` | As input | +| `'json+buffer'` | Any JSON type | `'buffer'` | As input | +| `'hex+view'` 1 | String (hex), Buffer | `'view'` | String (hex) | +| `'base64+view'` 1 | String (base64), Buffer | `'view'` | String (base64) | + +1 Unlike other encodings that transcode to `'view'`, these depend on Buffer at the moment and thus don't work in browsers if a [shim](https://github.com/feross/buffer) is not included by JavaScript bundlers like Webpack and Browserify. + +### Ecosystem Encodings + +Various modules in the ecosystem, in and outside of Level, can be used with `level-transcoder` although they follow different interfaces. Common between the interfaces is that they have `encode()` and `decode()` methods. The terms "codec" and "encoding" are used interchangeably in the ecosystem. Passing these encodings through `Transcoder#encoding()` (which is done implicitly when used in an `abstract-level` database) results in normalized encoding objects as described further below. + +| Module | Format | Interface | Named | +| :----------------------------------------- | :--------------- | :---------------------------------- | :---- | +| [`protocol-buffers`][protocol-buffers] | `buffer` | [`level-codec`][level-codec] | ❌ | +| [`charwise`][charwise] | `utf8` | [`level-codec`][level-codec] | ✅ | +| [`bytewise`][bytewise] | `buffer` | [`level-codec`][level-codec] | ✅ | +| [`lexicographic-integer-encoding`][lexint] | `buffer`, `utf8` | [`level-codec`][level-codec] | ✅ | +| [`abstract-encoding`][abstract-enc] | `buffer` | [`abstract-encoding`][abstract-enc] | ❌ | +| [`multiformats`][js-multiformats] | `view` | [`multiformats`][blockcodec] | ✅ | + +Those marked as not named are modules that export or generate encodings that don't have a `name` property (or `type` as an alias). We call these _anonymous encodings_. They can only be used as objects and not by name. Passing an anonymous encoding through `Transcoder#encoding()` does give it a `name` property for compatibility, but the value of `name` is not deterministic. + +## API + +### `Transcoder` + +#### `transcoder = new Transcoder(formats)` + +Create a new transcoder, providing the formats that are supported by a database (or other). The `formats` argument must be an array containing one or more of `'buffer'`, `'view'`, `'utf8'`. The returned `transcoder` instance is stateful, in that it contains a set of cached encoding objects. + +#### `encoding = transcoder.encoding(encoding)` + +Returns the given `encoding` argument as a normalized encoding object that follows the `level-transcoder` encoding interface. The `encoding` argument may be: + +- A string to select a known encoding by its name +- An object that follows one of the following interfaces: [`level-transcoder`](#encoding-interface), [`level-codec`](https://github.com/Level/codec#encoding-format), [`abstract-encoding`][abstract-enc], [`multiformats`][blockcodec] +- A previously normalized encoding, such that `encoding(x)` equals `encoding(encoding(x))`. + +Results are cached. If the `encoding` argument is an object and it has a name then subsequent calls can refer to that encoding by name. + +Depending on the `formats` provided to the `Transcoder` constructor, this method may return a _transcoder encoding_ that translates the desired encoding from / to a supported format. Its `encode()` and `decode()` methods will have respectively the same input and output types as a non-transcoded encoding, but its `name` property will differ. + +#### `encodings = transcoder.encodings()` + +Get an array of encoding objects. This includes: + +- Encodings for the `formats` that were passed to the `Transcoder` constructor +- Custom encodings that were passed to `transcoder.encoding()` +- Transcoder encodings for either. + +### `Encoding` + +#### `data = encoding.encode(data)` + +Encode data. + +#### `data = encoding.decode(data)` + +Decode data. + +#### `encoding.name` + +Unique name. A string. + +#### `encoding.commonName` + +Common name, computed from `name`. If this encoding is a transcoder encoding, `name` will be for example `'json+view'` and `commonName` will be just `'json'`. Else `name` will equal `commonName`. + +#### `encoding.format` + +Name of the (lower-level) encoding used by the return value of `encode()`. One of `'buffer'`, `'view'`, `'utf8'`. If `name` equals `format` then the encoding can be assumed to be idempotent, such that `encode(x)` equals `encode(encode(x))`. + +## Encoding Interface + +Custom encodings must follow the following interface: + +```ts +interface IEncoding { + name: string + format: 'buffer' | 'view' | 'utf8' + encode: (data: TIn) => TFormat + decode: (data: TFormat) => TOut +} +``` + +## Install + +With [npm](https://npmjs.org) do: + +``` +npm install level-transcoder +``` + +## Contributing + +[`Level/transcoder`](https://github.com/Level/transcoder) is an **OPEN Open Source Project**. This means that: + +> Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project. + +See the [Contribution Guide](https://github.com/Level/community/blob/master/CONTRIBUTING.md) for more details. + +## Donate + +Support us with a monthly donation on [Open Collective](https://opencollective.com/level) and help us continue our work. + +## License + +[MIT](LICENSE) + +[level-badge]: https://leveljs.org/img/badge.svg + +[level-codec]: https://github.com/Level/codec + +[leveldown]: https://github.com/Level/leveldown + +[protocol-buffers]: https://github.com/mafintosh/protocol-buffers + +[charwise]: https://github.com/dominictarr/charwise + +[bytewise]: https://github.com/deanlandolt/bytewise + +[lexint]: https://github.com/vweevers/lexicographic-integer-encoding + +[abstract-enc]: https://github.com/mafintosh/abstract-encoding + +[js-multiformats]: https://github.com/multiformats/js-multiformats + +[blockcodec]: https://github.com/multiformats/js-multiformats/blob/master/src/codecs/interface.ts diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/UPGRADING.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/UPGRADING.md new file mode 100644 index 0000000..7ecfbbc --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/UPGRADING.md @@ -0,0 +1,13 @@ +# Upgrade Guide + +This document describes breaking changes and how to upgrade. For a complete list of changes including minor and patch releases, please refer to the [changelog](CHANGELOG.md). + +## 1.0.0 + +This is the initial release of `level-transcoder`, which was forked from `level-codec`. Ultimately `level-transcoder` got a completely different API, so the two modules are not interchangeable. That said, here are the high-level differences from `level-codec` just for the record: + +- Throws if an encoding is not found, rather than falling back to `'id'` encoding +- The `'binary'` encoding has been renamed to `'buffer'`, with `'binary'` as an alias +- The `'utf8'` encoding of `level-codec` did not touch Buffers. In `level-transcoder` the same encoding will call `buffer.toString('utf8')` for consistency. Consumers can use the `'buffer'` encoding to avoid this conversion. +- The `'id'` encoding (aliased as `'none'`) which wasn't supported by any active `abstract-leveldown` implementation, has been removed. +- The `'ascii'`, `'ucs2'` and `'utf16le'` encodings are not supported. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/index.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/index.d.ts new file mode 100644 index 0000000..c230d72 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/index.d.ts @@ -0,0 +1,27 @@ +import { Encoding, MixedEncoding, KnownEncoding, KnownEncodingName } from './lib/encoding' + +export class Transcoder { + /** + * Create a Transcoder. + * @param formats Formats supported by consumer. + */ + constructor (formats: Array<'buffer'|'view'|'utf8'>) + + /** + * Get an array of supported encoding objects. + */ + encodings (): Array> + + /** + * Get the given encoding, creating a transcoder encoding if necessary. + * @param encoding Named encoding or encoding object. + */ + encoding ( + encoding: MixedEncoding + ): Encoding + + encoding (encoding: N): KnownEncoding + encoding (encoding: string): Encoding +} + +export * from './lib/encoding' diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/index.js new file mode 100644 index 0000000..17a2322 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/index.js @@ -0,0 +1,158 @@ +'use strict' + +const ModuleError = require('module-error') +const encodings = require('./lib/encodings') +const { Encoding } = require('./lib/encoding') +const { BufferFormat, ViewFormat, UTF8Format } = require('./lib/formats') + +const kFormats = Symbol('formats') +const kEncodings = Symbol('encodings') +const validFormats = new Set(['buffer', 'view', 'utf8']) + +/** @template T */ +class Transcoder { + /** + * @param {Array<'buffer'|'view'|'utf8'>} formats + */ + constructor (formats) { + if (!Array.isArray(formats)) { + throw new TypeError("The first argument 'formats' must be an array") + } else if (!formats.every(f => validFormats.has(f))) { + // Note: we only only support aliases in key- and valueEncoding options (where we already did) + throw new TypeError("Format must be one of 'buffer', 'view', 'utf8'") + } + + /** @type {Map, Encoding>} */ + this[kEncodings] = new Map() + this[kFormats] = new Set(formats) + + // Register encodings (done early in order to populate encodings()) + for (const k in encodings) { + try { + this.encoding(k) + } catch (err) { + /* istanbul ignore if: assertion */ + if (err.code !== 'LEVEL_ENCODING_NOT_SUPPORTED') throw err + } + } + } + + /** + * @returns {Array>} + */ + encodings () { + return Array.from(new Set(this[kEncodings].values())) + } + + /** + * @param {string|MixedEncoding} encoding + * @returns {Encoding} + */ + encoding (encoding) { + let resolved = this[kEncodings].get(encoding) + + if (resolved === undefined) { + if (typeof encoding === 'string' && encoding !== '') { + resolved = lookup[encoding] + + if (!resolved) { + throw new ModuleError(`Encoding '${encoding}' is not found`, { + code: 'LEVEL_ENCODING_NOT_FOUND' + }) + } + } else if (typeof encoding !== 'object' || encoding === null) { + throw new TypeError("First argument 'encoding' must be a string or object") + } else { + resolved = from(encoding) + } + + const { name, format } = resolved + + if (!this[kFormats].has(format)) { + if (this[kFormats].has('view')) { + resolved = resolved.createViewTranscoder() + } else if (this[kFormats].has('buffer')) { + resolved = resolved.createBufferTranscoder() + } else if (this[kFormats].has('utf8')) { + resolved = resolved.createUTF8Transcoder() + } else { + throw new ModuleError(`Encoding '${name}' cannot be transcoded`, { + code: 'LEVEL_ENCODING_NOT_SUPPORTED' + }) + } + } + + for (const k of [encoding, name, resolved.name, resolved.commonName]) { + this[kEncodings].set(k, resolved) + } + } + + return resolved + } +} + +exports.Transcoder = Transcoder + +/** + * @param {MixedEncoding} options + * @returns {Encoding} + */ +function from (options) { + if (options instanceof Encoding) { + return options + } + + // Loosely typed for ecosystem compatibility + const maybeType = 'type' in options && typeof options.type === 'string' ? options.type : undefined + const name = options.name || maybeType || `anonymous-${anonymousCount++}` + + switch (detectFormat(options)) { + case 'view': return new ViewFormat({ ...options, name }) + case 'utf8': return new UTF8Format({ ...options, name }) + case 'buffer': return new BufferFormat({ ...options, name }) + default: { + throw new TypeError("Format must be one of 'buffer', 'view', 'utf8'") + } + } +} + +/** + * If format is not provided, fallback to detecting `level-codec` + * or `multiformats` encodings, else assume a format of buffer. + * @param {MixedEncoding} options + * @returns {string} + */ +function detectFormat (options) { + if ('format' in options && options.format !== undefined) { + return options.format + } else if ('buffer' in options && typeof options.buffer === 'boolean') { + return options.buffer ? 'buffer' : 'utf8' // level-codec + } else if ('code' in options && Number.isInteger(options.code)) { + return 'view' // multiformats + } else { + return 'buffer' + } +} + +/** + * @typedef {import('./lib/encoding').MixedEncoding} MixedEncoding + * @template TIn, TFormat, TOut + */ + +/** + * @type {Object.>} + */ +const aliases = { + binary: encodings.buffer, + 'utf-8': encodings.utf8 +} + +/** + * @type {Object.>} + */ +const lookup = { + ...encodings, + ...aliases +} + +let anonymousCount = 0 diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/encoding.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/encoding.d.ts new file mode 100644 index 0000000..c52f9ce --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/encoding.d.ts @@ -0,0 +1,158 @@ +import { BufferFormat, ViewFormat, UTF8Format } from './formats' + +/** + * Encodes {@link TIn} to {@link TFormat} and decodes + * {@link TFormat} to {@link TOut}. + */ +export abstract class Encoding implements IEncoding { + constructor (options: IEncoding) + + encode: (data: TIn) => TFormat + decode: (data: TFormat) => TOut + name: string + format: 'buffer' | 'view' | 'utf8' + createViewTranscoder (): ViewFormat + createBufferTranscoder (): BufferFormat + createUTF8Transcoder (): UTF8Format + + /** + * Common name, computed from {@link name}. If this encoding is a + * transcoder encoding, {@link name} will be for example 'json+view' + * and {@link commonName} will be just 'json'. Else {@link name} + * will equal {@link commonName}. + */ + get commonName (): string +} + +export interface IEncoding { + /** + * Encode data. + */ + encode: (data: TIn) => TFormat + + /** + * Decode data. + */ + decode: (data: TFormat) => TOut + + /** + * Unique name. + */ + name: string + + /** + * The name of the (lower-level) encoding used by the return value of + * {@link encode}. One of 'buffer', 'view', 'utf8'. + */ + format: 'buffer' | 'view' | 'utf8' + + /** + * Create a new encoding that transcodes {@link TFormat} from / to a view. + */ + createViewTranscoder?: (() => ViewFormat) | undefined + + /** + * Create a new encoding that transcodes {@link TFormat} from / to a buffer. + */ + createBufferTranscoder?: (() => BufferFormat) | undefined + + /** + * Create a new encoding that transcodes {@link TFormat} from / to a UTF-8 string. + */ + createUTF8Transcoder?: (() => UTF8Format) | undefined +} + +export interface IExternalEncoding { + /** + * Encode data. + */ + encode: (data: TIn) => TFormat + + /** + * Decode data. + */ + decode: (data: TFormat) => TOut + + /** + * Unique name. + */ + name?: string | undefined + + /** + * Legacy `level-codec` option that means the same as `format: 'buffer'` + * if true or `format: 'utf8'` if false. + */ + buffer?: boolean | undefined + + /** + * Legacy `level-codec` alias for {@link name}. Used only when the + * {@link name} option is undefined. + */ + type?: any + + /** + * To detect `multiformats`. If a number, then the encoding is + * assumed to have a {@link format} of 'view'. + * @see https://github.com/multiformats/js-multiformats/blob/master/src/codecs/interface.ts + */ + code?: any +} + +/** + * Names of built-in encodings. + */ +export type KnownEncodingName = 'utf8' | 'buffer' | 'view' | 'json' | 'hex' | 'base64' + +/** + * One of the supported encoding interfaces. + */ +export type MixedEncoding = + IEncoding | + IExternalEncoding + +/** + * Type utility to cast a built-in encoding identified by its name to an {@link Encoding}. + */ +export type KnownEncoding + = Encoding, TFormat, KnownEncodingOutput> + +/** + * Type utility to get the input type of a built-in encoding identified by its name. + */ +export type KnownEncodingInput + = N extends 'utf8' ? string | Buffer | Uint8Array + : N extends 'buffer' ? Buffer | Uint8Array | string + : N extends 'view' ? Uint8Array | string + : N extends 'json' ? any + : N extends 'hex' ? Buffer | string + : N extends 'base64' ? Buffer | string + : never + +/** + * Type utility to get the output type of a built-in encoding identified by its name. + */ +export type KnownEncodingOutput + = N extends 'utf8' ? string + : N extends 'buffer' ? Buffer + : N extends 'view' ? Uint8Array + : N extends 'json' ? any + : N extends 'hex' ? string + : N extends 'base64' ? string + : never + +/** + * Type utility to use a {@link MixedEncoding} with an untyped format. + */ +export type PartialEncoding = MixedEncoding + +/** + * Type utility to use a {@link MixedEncoding} with an untyped format and output. + * For when only the encoding side is needed. + */ +export type PartialEncoder = MixedEncoding + +/** + * Type utility to use a {@link MixedEncoding} with an untyped input and format. + * For when only the decoding side is needed. + */ +export type PartialDecoder = MixedEncoding diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/encoding.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/encoding.js new file mode 100644 index 0000000..297a039 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/encoding.js @@ -0,0 +1,105 @@ +'use strict' + +const ModuleError = require('module-error') +const formats = new Set(['buffer', 'view', 'utf8']) + +/** + * @template TIn, TFormat, TOut + * @abstract + */ +class Encoding { + /** + * @param {IEncoding} options + */ + constructor (options) { + /** @type {(data: TIn) => TFormat} */ + this.encode = options.encode || this.encode + + /** @type {(data: TFormat) => TOut} */ + this.decode = options.decode || this.decode + + /** @type {string} */ + this.name = options.name || this.name + + /** @type {string} */ + this.format = options.format || this.format + + if (typeof this.encode !== 'function') { + throw new TypeError("The 'encode' property must be a function") + } + + if (typeof this.decode !== 'function') { + throw new TypeError("The 'decode' property must be a function") + } + + this.encode = this.encode.bind(this) + this.decode = this.decode.bind(this) + + if (typeof this.name !== 'string' || this.name === '') { + throw new TypeError("The 'name' property must be a string") + } + + if (typeof this.format !== 'string' || !formats.has(this.format)) { + throw new TypeError("The 'format' property must be one of 'buffer', 'view', 'utf8'") + } + + if (options.createViewTranscoder) { + this.createViewTranscoder = options.createViewTranscoder + } + + if (options.createBufferTranscoder) { + this.createBufferTranscoder = options.createBufferTranscoder + } + + if (options.createUTF8Transcoder) { + this.createUTF8Transcoder = options.createUTF8Transcoder + } + } + + get commonName () { + return /** @type {string} */ (this.name.split('+')[0]) + } + + /** @return {BufferFormat} */ + createBufferTranscoder () { + throw new ModuleError(`Encoding '${this.name}' cannot be transcoded to 'buffer'`, { + code: 'LEVEL_ENCODING_NOT_SUPPORTED' + }) + } + + /** @return {ViewFormat} */ + createViewTranscoder () { + throw new ModuleError(`Encoding '${this.name}' cannot be transcoded to 'view'`, { + code: 'LEVEL_ENCODING_NOT_SUPPORTED' + }) + } + + /** @return {UTF8Format} */ + createUTF8Transcoder () { + throw new ModuleError(`Encoding '${this.name}' cannot be transcoded to 'utf8'`, { + code: 'LEVEL_ENCODING_NOT_SUPPORTED' + }) + } +} + +exports.Encoding = Encoding + +/** + * @typedef {import('./encoding').IEncoding} IEncoding + * @template TIn, TFormat, TOut + */ + +/** + * @typedef {import('./formats').BufferFormat} BufferFormat + * @template TIn, TOut + */ + +/** + * @typedef {import('./formats').ViewFormat} ViewFormat + * @template TIn, TOut + */ + +/** + * @typedef {import('./formats').UTF8Format} UTF8Format + * @template TIn, TOut + */ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/encodings.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/encodings.d.ts new file mode 100644 index 0000000..335fdba --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/encodings.d.ts @@ -0,0 +1,9 @@ +import { KnownEncodingInput, KnownEncodingOutput } from './encoding' +import { BufferFormat, ViewFormat, UTF8Format } from './formats' + +export const utf8: UTF8Format, KnownEncodingOutput<'utf8'>> +export const json: UTF8Format, KnownEncodingOutput<'json'>> +export const buffer: BufferFormat, KnownEncodingOutput<'buffer'>> +export const view: ViewFormat, KnownEncodingOutput<'view'>> +export const hex: BufferFormat, KnownEncodingOutput<'hex'>> +export const base64: BufferFormat, KnownEncodingOutput<'base64'>> diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/encodings.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/encodings.js new file mode 100644 index 0000000..f055d9d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/encodings.js @@ -0,0 +1,135 @@ +'use strict' + +const { Buffer } = require('buffer') || { Buffer: { isBuffer: () => false } } +const { textEncoder, textDecoder } = require('./text-endec')() +const { BufferFormat, ViewFormat, UTF8Format } = require('./formats') + +/** @type {(v: T) => v} */ +const identity = (v) => v + +/** + * @type {typeof import('./encodings').utf8} + */ +exports.utf8 = new UTF8Format({ + encode: function (data) { + // On node 16.9.1 buffer.toString() is 5x faster than TextDecoder + return Buffer.isBuffer(data) + ? data.toString('utf8') + : ArrayBuffer.isView(data) + ? textDecoder.decode(data) + : String(data) + }, + decode: identity, + name: 'utf8', + createViewTranscoder () { + return new ViewFormat({ + encode: function (data) { + return ArrayBuffer.isView(data) ? data : textEncoder.encode(data) + }, + decode: function (data) { + return textDecoder.decode(data) + }, + name: `${this.name}+view` + }) + }, + createBufferTranscoder () { + return new BufferFormat({ + encode: function (data) { + return Buffer.isBuffer(data) + ? data + : ArrayBuffer.isView(data) + ? Buffer.from(data.buffer, data.byteOffset, data.byteLength) + : Buffer.from(String(data), 'utf8') + }, + decode: function (data) { + return data.toString('utf8') + }, + name: `${this.name}+buffer` + }) + } +}) + +/** + * @type {typeof import('./encodings').json} + */ +exports.json = new UTF8Format({ + encode: JSON.stringify, + decode: JSON.parse, + name: 'json' +}) + +/** + * @type {typeof import('./encodings').buffer} + */ +exports.buffer = new BufferFormat({ + encode: function (data) { + return Buffer.isBuffer(data) + ? data + : ArrayBuffer.isView(data) + ? Buffer.from(data.buffer, data.byteOffset, data.byteLength) + : Buffer.from(String(data), 'utf8') + }, + decode: identity, + name: 'buffer', + createViewTranscoder () { + return new ViewFormat({ + encode: function (data) { + return ArrayBuffer.isView(data) ? data : Buffer.from(String(data), 'utf8') + }, + decode: function (data) { + return Buffer.from(data.buffer, data.byteOffset, data.byteLength) + }, + name: `${this.name}+view` + }) + } +}) + +/** + * @type {typeof import('./encodings').view} + */ +exports.view = new ViewFormat({ + encode: function (data) { + return ArrayBuffer.isView(data) ? data : textEncoder.encode(data) + }, + decode: identity, + name: 'view', + createBufferTranscoder () { + return new BufferFormat({ + encode: function (data) { + return Buffer.isBuffer(data) + ? data + : ArrayBuffer.isView(data) + ? Buffer.from(data.buffer, data.byteOffset, data.byteLength) + : Buffer.from(String(data), 'utf8') + }, + decode: identity, + name: `${this.name}+buffer` + }) + } +}) + +/** + * @type {typeof import('./encodings').hex} + */ +exports.hex = new BufferFormat({ + encode: function (data) { + return Buffer.isBuffer(data) ? data : Buffer.from(String(data), 'hex') + }, + decode: function (buffer) { + return buffer.toString('hex') + }, + name: 'hex' +}) + +/** + * @type {typeof import('./encodings').base64} + */ +exports.base64 = new BufferFormat({ + encode: function (data) { + return Buffer.isBuffer(data) ? data : Buffer.from(String(data), 'base64') + }, + decode: function (buffer) { + return buffer.toString('base64') + }, + name: 'base64' +}) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/formats.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/formats.d.ts new file mode 100644 index 0000000..4cb2775 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/formats.d.ts @@ -0,0 +1,13 @@ +import { Encoding, IEncoding } from './encoding' + +export class BufferFormat extends Encoding { + constructor (options: Omit, 'format'>) +} + +export class ViewFormat extends Encoding { + constructor (options: Omit, 'format'>) +} + +export class UTF8Format extends Encoding { + constructor (options: Omit, 'format'>) +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/formats.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/formats.js new file mode 100644 index 0000000..0f1fe2e --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/formats.js @@ -0,0 +1,111 @@ +'use strict' + +const { Buffer } = require('buffer') || {} +const { Encoding } = require('./encoding') +const textEndec = require('./text-endec') + +/** + * @template TIn, TOut + * @extends {Encoding} + */ +class BufferFormat extends Encoding { + /** + * @param {Omit, 'format'>} options + */ + constructor (options) { + super({ ...options, format: 'buffer' }) + } + + /** @override */ + createViewTranscoder () { + return new ViewFormat({ + encode: this.encode, // Buffer is a view (UInt8Array) + decode: (data) => this.decode( + Buffer.from(data.buffer, data.byteOffset, data.byteLength) + ), + name: `${this.name}+view` + }) + } + + /** @override */ + createBufferTranscoder () { + return this + } +} + +/** + * @extends {Encoding} + * @template TIn, TOut + */ +class ViewFormat extends Encoding { + /** + * @param {Omit, 'format'>} options + */ + constructor (options) { + super({ ...options, format: 'view' }) + } + + /** @override */ + createBufferTranscoder () { + return new BufferFormat({ + encode: (data) => { + const view = this.encode(data) + return Buffer.from(view.buffer, view.byteOffset, view.byteLength) + }, + decode: this.decode, // Buffer is a view (UInt8Array) + name: `${this.name}+buffer` + }) + } + + /** @override */ + createViewTranscoder () { + return this + } +} + +/** + * @extends {Encoding} + * @template TIn, TOut + */ +class UTF8Format extends Encoding { + /** + * @param {Omit, 'format'>} options + */ + constructor (options) { + super({ ...options, format: 'utf8' }) + } + + /** @override */ + createBufferTranscoder () { + return new BufferFormat({ + encode: (data) => Buffer.from(this.encode(data), 'utf8'), + decode: (data) => this.decode(data.toString('utf8')), + name: `${this.name}+buffer` + }) + } + + /** @override */ + createViewTranscoder () { + const { textEncoder, textDecoder } = textEndec() + + return new ViewFormat({ + encode: (data) => textEncoder.encode(this.encode(data)), + decode: (data) => this.decode(textDecoder.decode(data)), + name: `${this.name}+view` + }) + } + + /** @override */ + createUTF8Transcoder () { + return this + } +} + +exports.BufferFormat = BufferFormat +exports.ViewFormat = ViewFormat +exports.UTF8Format = UTF8Format + +/** + * @typedef {import('./encoding').IEncoding} IEncoding + * @template TIn, TFormat, TOut + */ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/text-endec.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/text-endec.d.ts new file mode 100644 index 0000000..997a861 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/text-endec.d.ts @@ -0,0 +1,6 @@ +declare function textEndec (): { + textEncoder: TextEncoder + textDecoder: TextDecoder +} + +export = textEndec diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/text-endec.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/text-endec.js new file mode 100644 index 0000000..7d9bc02 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/lib/text-endec.js @@ -0,0 +1,19 @@ +'use strict' + +/** @type {{ textEncoder: TextEncoder, textDecoder: TextDecoder }|null} */ +let lazy = null + +/** + * Get semi-global instances of TextEncoder and TextDecoder. + * @returns {{ textEncoder: TextEncoder, textDecoder: TextDecoder }} + */ +module.exports = function () { + if (lazy === null) { + lazy = { + textEncoder: new TextEncoder(), + textDecoder: new TextDecoder() + } + } + + return lazy +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/package.json new file mode 100644 index 0000000..ac34a76 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level-transcoder/package.json @@ -0,0 +1,46 @@ +{ + "name": "level-transcoder", + "version": "1.0.1", + "description": "Encode data with built-in or custom encodings", + "license": "MIT", + "main": "index.js", + "types": "./index.d.ts", + "scripts": { + "test": "ts-standard && tsc && standard && hallmark && nyc tape test/*.js", + "test-browsers-local": "airtap --coverage --verbose test/*.js", + "coverage": "nyc report -r lcovonly", + "hallmark": "hallmark --fix" + }, + "files": [ + "lib", + "index.js", + "index.d.ts", + "CHANGELOG.md", + "LICENSE", + "UPGRADING.md" + ], + "dependencies": { + "buffer": "^6.0.3", + "module-error": "^1.0.1" + }, + "devDependencies": { + "@types/node": "^16.11.10", + "@voxpelli/tsconfig": "^3.1.0", + "airtap": "^4.0.3", + "airtap-playwright": "^1.0.1", + "hallmark": "^4.0.0", + "nyc": "^15.1.0", + "standard": "^16.0.3", + "tape": "^5.3.2", + "ts-standard": "^11.0.0", + "typescript": "^4.5.2" + }, + "repository": "Level/transcoder", + "homepage": "https://github.com/Level/transcoder", + "keywords": [ + "level" + ], + "engines": { + "node": ">=12" + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/CHANGELOG.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/CHANGELOG.md new file mode 100644 index 0000000..341cd46 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/CHANGELOG.md @@ -0,0 +1,512 @@ +# Changelog + +## [8.0.1] - 2024-01-27 + +### Fixed + +- Explicitly depend on abstract-level for TypeScript ([#241](https://github.com/Level/level/issues/241)) ([`c501868`](https://github.com/Level/level/commit/c501868)) (Hanxx). + +## [8.0.0] - 2022-03-25 + +_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ + +### Changed + +- **Breaking:** switch to `classic-level` and `browser-level` ([#215](https://github.com/Level/level/issues/215)) ([`ad22b21`](https://github.com/Level/level/commit/ad22b21)) (Vincent Weevers). + +## [7.0.1] - 2021-10-02 + +### Added + +- Document new features ([#207](https://github.com/Level/level/issues/207)) ([`ad8f924`](https://github.com/Level/level/commit/ad8f924)) (Vincent Weevers) + +### Fixed + +- Bump dependencies to prevent dedupe ([`7083ec6`](https://github.com/Level/level/commit/7083ec6)) (Vincent Weevers) +- Clarify `close()` documentation ([#197](https://github.com/Level/level/issues/197)) ([`c82fdbc`](https://github.com/Level/level/commit/c82fdbc)) (Vincent Weevers) + +## [7.0.0] - 2021-04-17 + +_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ + +### Changed + +- **Breaking:** bump `leveldown` and `level-packager` ([`53bd922`](https://github.com/Level/level/commit/53bd922)) (Vincent Weevers) +- **Breaking:** bump `level-js` from 5.x to 6.x ([#194](https://github.com/Level/level/issues/194)) ([`1f6c603`](https://github.com/Level/level/commit/1f6c603)) (Alex Potsides) +- **Breaking:** modernize syntax ([Level/community#98](https://github.com/Level/community/issues/98)) ([`d001b2c`](https://github.com/Level/level/commit/d001b2c)) (Vincent Weevers) +- Add `files` to `package.json` and remove `.npmignore` ([`329e1f5`](https://github.com/Level/level/commit/329e1f5)) (Vincent Weevers) + +### Added + +- Document chained batch encoding options ([Level/levelup#633](https://github.com/Level/levelup/issues/633)) ([`0b3c11d`](https://github.com/Level/level/commit/0b3c11d)) (Vincent Weevers) +- Document the `clear` event ([Level/community#79](https://github.com/Level/community/issues/79)) ([`52314bf`](https://github.com/Level/level/commit/52314bf)) (Vincent Weevers) + +### Removed + +- **Breaking:** drop node 8 ([Level/community#98](https://github.com/Level/community/issues/98)) ([`f8a0047`](https://github.com/Level/level/commit/f8a0047), [`31317a6`](https://github.com/Level/level/commit/31317a6)) (Vincent Weevers) +- Remove legacy range options from README ([Level/community#86](https://github.com/Level/community/issues/86)) ([`e56c6b1`](https://github.com/Level/level/commit/e56c6b1)) (Vincent Weevers) + +## [6.0.1] - 2020-03-04 + +### Changed + +- Switch from `opencollective-postinstall` to npm `funding` ([#173](https://github.com/Level/level/issues/173)) ([**@Richienb**](https://github.com/Richienb)) +- Upgrade `nyc` devDependency from `^14.0.0` to `^15.0.0` ([#169](https://github.com/Level/level/issues/169)) ([**@vweevers**](https://github.com/vweevers)) +- Upgrade `airtap` devDependency from `^2.0.1` to `^3.0.0` ([#171](https://github.com/Level/level/issues/171)) ([**@vweevers**](https://github.com/vweevers)) + +## [6.0.0] - 2019-10-19 + +_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ + +### Changed + +- **Breaking:** upgrade `level-js` from `^4.0.0` to `^5.0.0` ([#158](https://github.com/Level/level/issues/158)) ([**@vweevers**](https://github.com/vweevers)) +- Upgrade `hallmark` devDependency from `^0.1.0` to `^2.0.0` ([#152](https://github.com/Level/level/issues/152), [#157](https://github.com/Level/level/issues/157)) ([**@vweevers**](https://github.com/vweevers)) +- Upgrade `standard` devDependency from `^12.0.0` to `^14.0.0` ([#151](https://github.com/Level/level/issues/151), [#155](https://github.com/Level/level/issues/155)) ([**@vweevers**](https://github.com/vweevers)) +- Upgrade `nyc` devDependency from `^13.2.0` to `^14.0.0` ([#147](https://github.com/Level/level/issues/147)) ([**@vweevers**](https://github.com/vweevers)) + +### Added + +- Document manifest, `iterator()` and `clear()` ([#162](https://github.com/Level/level/issues/162)) ([**@vweevers**](https://github.com/vweevers)) +- Add links to `browserify-starter` and `webpack-starter` ([`6ff1802`](https://github.com/Level/level/commit/6ff1802)) ([**@vweevers**](https://github.com/vweevers)) + +### Fixed + +- Bump `leveldown` and `level-packager` to prevent dedupe ([`5efdc82`](https://github.com/Level/level/commit/5efdc82), [`a9656c3`](https://github.com/Level/level/commit/a9656c3), [`97cb31c`](https://github.com/Level/level/commit/97cb31c)) ([**@vweevers**](https://github.com/vweevers)) + +## [5.0.1] - 2019-03-29 + +### Fixed + +- Temporarily skip `hallmark` test because it breaks CITGM ([#145](https://github.com/Level/level/issues/145)) ([**@vweevers**](https://github.com/vweevers)) + +## [5.0.0] - 2019-03-29 + +_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ + +### Changed + +- Upgrade `leveldown` from `^4.0.0` to `^5.0.0` ([#133](https://github.com/Level/level/issues/133), [#144](https://github.com/Level/level/issues/144)) ([**@vweevers**](https://github.com/vweevers)) +- Upgrade `level-packager` from `^3.0.0` to `^5.0.0` ([#113](https://github.com/Level/level/issues/113), [#131](https://github.com/Level/level/issues/131)) ([**@ralphtheninja**](https://github.com/ralphtheninja), [**@vweevers**](https://github.com/vweevers)) +- Prefer `var` over `const` in README ([`f032b6c`](https://github.com/Level/level/commit/f032b6c)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Upgrade `standard` devDependency from `^11.0.0` to `^12.0.0` ([#118](https://github.com/Level/level/issues/118)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Tweak copyright years for less maintenance ([`0b9c8ad`](https://github.com/Level/level/commit/0b9c8ad)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Tweak Open Collective documentation ([#137](https://github.com/Level/level/issues/137)) ([**@vweevers**](https://github.com/vweevers)) +- Apply common project tweaks ([#136](https://github.com/Level/level/issues/136), [#138](https://github.com/Level/level/issues/138)) ([**@vweevers**](https://github.com/vweevers)) +- Add `.travis.yml` and `appveyor.yml` to `.npmignore` ([`7b5c340`](https://github.com/Level/level/commit/7b5c340)) ([**@vweevers**](https://github.com/vweevers)) + +### Added + +- Integrate `level-js` for browser support ([#135](https://github.com/Level/level/issues/135)) ([**@vweevers**](https://github.com/vweevers)) +- Add appveyor ([#112](https://github.com/Level/level/issues/112)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Enable OSX on Travis ([#111](https://github.com/Level/level/issues/111)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Add `nyc` and `coveralls` devDependencies ([#115](https://github.com/Level/level/issues/115), [#143](https://github.com/Level/level/issues/143)) ([**@ralphtheninja**](https://github.com/ralphtheninja), [**@vweevers**](https://github.com/vweevers)) +- Add `hallmark` devDependency ([#134](https://github.com/Level/level/issues/134)) ([**@vweevers**](https://github.com/vweevers)) +- Add note about Rollup to `README.md` ([#139](https://github.com/Level/level/issues/139)) ([**@vweevers**](https://github.com/vweevers)) + +### Removed + +- Remove node 6 and 9 ([#129](https://github.com/Level/level/issues/129), [`2bf1d3f`](https://github.com/Level/level/commit/2bf1d3f)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Remove contributors from `package.json` ([`f37252d`](https://github.com/Level/level/commit/f37252d)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [4.0.0] - 2018-05-23 + +_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ + +### Changed + +- Update `leveldown` to `^4.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Update `level-packager` to `^3.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Switch to `opencollective-postinstall` ([**@mateodelnorte**](https://github.com/mateodelnorte)) + +### Removed + +- Remove node 4 from Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [3.0.2] - 2018-05-23 + +### Changed + +- Switch to `opencollective-postinstall` ([**@mateodelnorte**](https://github.com/mateodelnorte)) + +## [3.0.1] - 2018-05-05 + +### Added + +- Travis: add 10 ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +### Changed + +- Update `standard` to `^11.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Fix typo in README ([**@rasmuserik**](https://github.com/rasmuserik)) + +### Fixed + +- Fix postinstall failures with OpenCollective ([**@vweevers**](https://github.com/vweevers)) + +## [3.0.0] - 2018-02-17 + +_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ + +### Added + +- Travis: add 9 ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- README: add table of contents ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +### Changed + +- Update `leveldown` to `^3.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [2.1.2] - 2018-01-26 + +### Added + +- Add OpenCollective ([**@monkeywithacupcake**](https://github.com/monkeywithacupcake)) + +### Changed + +- README: change Travis badge from png to svg ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [2.1.1] - 2017-12-13 + +### Changed + +- README: document `.errors` property ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [2.1.0] - 2017-12-06 + +### Changed + +- Update `level-packager` to `^2.0.2` ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Update `leveldown` to `^2.1.1` ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [2.0.1] - 2017-11-11 + +### Changed + +- Restore node 4 ([**@vweevers**](https://github.com/vweevers)) + +## [2.0.0] - 2017-10-17 + +_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ + +### Added + +- Add `standard` for linting ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- README: copy over docs from `levelup` ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- README: add node badge ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +### Changed + +- Update `level-packager` to `~2.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Update `leveldown` to `~2.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [2.0.0-rc3] - 2017-09-16 + +### Changed + +- Update `level-packager` to `2.0.0-rc3` ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Update `leveldown` to `~1.8.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [2.0.0-rc2] - 2017-09-12 + +### Changed + +- Update `level-packager` to `2.0.0-rc2` ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Update `leveldown` to `~1.7.2` ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [2.0.0-rc1] - 2017-09-06 + +### Added + +- README: add Greenkeeper badge ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Travis: add 8 ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +### Changed + +- Update `level-packager` to `2.0.0-rc1` ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +### Removed + +- Travis: remove 0.12, 4, 5, and 7 ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [1.7.0] - 2017-05-17 + +### Changed + +- Update `leveldown` to `~1.7.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [1.6.0] - 2017-02-06 + +### Added + +- Travis: add 7 ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +### Changed + +- Update copyright year to 2017 ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Update `leveldown` to `~1.6.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +### Removed + +- Travis: remove 0.10 ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [1.5.0] - 2016-10-16 + +### Added + +- Travis: add 6 ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +### Changed + +- Use gcc 4.8 on Travis +- Update `leveldown` to `~1.5.0` ([**@juliangruber**](https://github.com/juliangruber)) + +### Removed + +- Travis: remove 1.0, 1.8, 2 and 3 ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [1.4.0] - 2015-11-27 + +### Added + +- Travis: add 1.0, 2, 3, 4 and 5 ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- README: add dependency badge ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +### Changed + +- Update `level-packager` to `~1.2.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [1.3.0] - 2015-07-29 + +### Changed + +- Update `leveldown` to `~1.4.0` ([**@ArtskydJ**](https://github.com/ArtskydJ)) + +## [1.2.0] - 2015-06-24 + +### Changed + +- Update `level-packager` to `~1.1.0` ([**@timoxley**](https://github.com/timoxley)) +- Update `leveldown` to `~1.3.0` ([**@timoxley**](https://github.com/timoxley)) + +## [1.1.0] - 2015-06-02 + +### Changed + +- Update `leveldown` to `~1.2.2` for prebuilt binaries ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [1.0.0] - 2015-05-19 + +### Changed + +- Update `level-packager` to `~1.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [1.0.0-0] - 2015-05-16 + +### Changed + +- Use `nvm` again on Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Moved `CONTRIBUTING.md` and contributors to `level/community` repository ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Use `level-packager@next` ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Update `leveldown` to `~1.0.6` ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [0.19.1] - 2015-05-05 + +### Changed + +- Use `n` instead of `nvm` on Travis for iojs support on native modules ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [0.19.0] - 2015-05-05 + +### Changed + +- Switch to plain MIT license ([**@andrewrk**](https://github.com/andrewrk)) +- README: update nodeico badge ([**@rvagg**](https://github.com/rvagg)) +- README: update logo and copyright ([**@ralphtheninja**](https://github.com/ralphtheninja)) +- Update `level-packager` to `~0.19.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) + +## [0.18.0] - 2013-11-18 + +### Added + +- Add Travis ([**@rvagg**](https://github.com/rvagg)) + +### Changed + +- Update `level-packager` to `~0.18.0` ([**@rvagg**](https://github.com/rvagg)) +- Update `leveldown` to `~0.10.0` ([**@rvagg**](https://github.com/rvagg)) + +## [0.17.0] - 2013-10-01 + +_0.17.0 and 0.17.0-1 are listed out of order here, due to 0.17.0-1 not adhering to the semver rules that we follow today._ + +### Changed + +- Update `levelup` to `~0.17.0` ([**@rvagg**](https://github.com/rvagg)) +- Update `leveldown` to `~0.9.0` ([**@rvagg**](https://github.com/rvagg)) + +## [0.17.0-1] - 2013-10-09 + +### Changed + +- Use `level-packager` instead of `levelup` ([**@rvagg**](https://github.com/rvagg)) +- Run tests in `level-packager` using `tape` ([**@rvagg**](https://github.com/rvagg)) + +## [0.16.0] - 2013-09-10 + +### Added + +- Add [**@substack**](https://github.com/substack) to contributors ([**@rvagg**](https://github.com/rvagg)) + +### Changed + +- Update `levelup` to `~0.16.0` ([**@rvagg**](https://github.com/rvagg)) +- Update repository and homepage in `package.json` ([**@rvagg**](https://github.com/rvagg)) + +## [0.15.0] - 2013-08-26 + +### Changed + +- README: tweaks ([**@rvagg**](https://github.com/rvagg)) +- Update `levelup` to `~0.15.0` ([**@rvagg**](https://github.com/rvagg)) +- Update `leveldown` to `~0.8.0` ([**@rvagg**](https://github.com/rvagg)) + +## [0.14.0] - 2013-08-19 + +### Added + +- README: add npm downloads badge ([**@rvagg**](https://github.com/rvagg)) + +### Changed + +- Update `levelup` to `~0.14.0` ([**@rvagg**](https://github.com/rvagg)) + +## [0.13.0] - 2013-08-11 + +### Changed + +- Update `levelup` to `~0.13.0` ([**@rvagg**](https://github.com/rvagg)) +- Update `leveldown` to `~0.7.0` ([**@rvagg**](https://github.com/rvagg)) + +## [0.12.0] - 2013-07-25 + +### Changed + +- Update `levelup` to `~0.12.0` ([**@rvagg**](https://github.com/rvagg)) +- Update `leveldown` to `~0.6.2` ([**@rvagg**](https://github.com/rvagg)) + +## [0.11.0] - 2013-07-17 + +### Added + +- Add [**@pgte**](https://github.com/pgte) to contributors ([**@rvagg**](https://github.com/rvagg)) +- README: add npm badge ([**@rvagg**](https://github.com/rvagg)) + +### Changed + +- Update `levelup` to `~0.11.0` ([**@rvagg**](https://github.com/rvagg)) + +## [0.10.0] - 2013-06-14 + +### Changed + +- Update `levelup` to `~0.10.0` ([**@rvagg**](https://github.com/rvagg)) +- Update `leveldown` to `~0.6.0` ([**@rvagg**](https://github.com/rvagg)) + +## [0.9.0] - 2013-05-27 + +### Changed + +- Update `levelup` to `~0.9.0` ([**@rvagg**](https://github.com/rvagg)) +- Update `leveldown` to `~0.5.0` ([**@rvagg**](https://github.com/rvagg)) + +## [0.8.0] - 2013-05-19 + +:seedling: Initial release. + +[8.0.1]: https://github.com/Level/level/releases/tag/v8.0.1 + +[8.0.0]: https://github.com/Level/level/releases/tag/v8.0.0 + +[7.0.1]: https://github.com/Level/level/releases/tag/v7.0.1 + +[7.0.0]: https://github.com/Level/level/releases/tag/v7.0.0 + +[6.0.1]: https://github.com/Level/level/releases/tag/v6.0.1 + +[6.0.0]: https://github.com/Level/level/releases/tag/v6.0.0 + +[5.0.1]: https://github.com/Level/level/releases/tag/v5.0.1 + +[5.0.0]: https://github.com/Level/level/releases/tag/v5.0.0 + +[4.0.0]: https://github.com/Level/level/releases/tag/v4.0.0 + +[3.0.2]: https://github.com/Level/level/releases/tag/v3.0.2 + +[3.0.1]: https://github.com/Level/level/releases/tag/v3.0.1 + +[3.0.0]: https://github.com/Level/level/releases/tag/v3.0.0 + +[2.1.2]: https://github.com/Level/level/releases/tag/v2.1.2 + +[2.1.1]: https://github.com/Level/level/releases/tag/v2.1.1 + +[2.1.0]: https://github.com/Level/level/releases/tag/v2.1.0 + +[2.0.1]: https://github.com/Level/level/releases/tag/v2.0.1 + +[2.0.0]: https://github.com/Level/level/releases/tag/v2.0.0 + +[2.0.0-rc3]: https://github.com/Level/level/releases/tag/v2.0.0-rc3 + +[2.0.0-rc2]: https://github.com/Level/level/releases/tag/v2.0.0-rc2 + +[2.0.0-rc1]: https://github.com/Level/level/releases/tag/v2.0.0-rc1 + +[1.7.0]: https://github.com/Level/level/releases/tag/v1.7.0 + +[1.6.0]: https://github.com/Level/level/releases/tag/v1.6.0 + +[1.5.0]: https://github.com/Level/level/releases/tag/v1.5.0 + +[1.4.0]: https://github.com/Level/level/releases/tag/v1.4.0 + +[1.3.0]: https://github.com/Level/level/releases/tag/v1.3.0 + +[1.2.0]: https://github.com/Level/level/releases/tag/v1.2.0 + +[1.1.0]: https://github.com/Level/level/releases/tag/v1.1.0 + +[1.0.0]: https://github.com/Level/level/releases/tag/v1.0.0 + +[1.0.0-0]: https://github.com/Level/level/releases/tag/v1.0.0-0 + +[0.19.1]: https://github.com/Level/level/releases/tag/v0.19.1 + +[0.19.0]: https://github.com/Level/level/releases/tag/v0.19.0 + +[0.18.0]: https://github.com/Level/level/releases/tag/0.18.0 + +[0.17.0]: https://github.com/Level/level/releases/tag/0.17.0 + +[0.17.0-1]: https://github.com/Level/level/releases/tag/0.17.0-1 + +[0.16.0]: https://github.com/Level/level/releases/tag/0.16.0 + +[0.15.0]: https://github.com/Level/level/releases/tag/0.15.0 + +[0.14.0]: https://github.com/Level/level/releases/tag/0.14.0 + +[0.13.0]: https://github.com/Level/level/releases/tag/0.13.0 + +[0.12.0]: https://github.com/Level/level/releases/tag/0.12.0 + +[0.11.0]: https://github.com/Level/level/releases/tag/0.11.0 + +[0.10.0]: https://github.com/Level/level/releases/tag/0.10.0 + +[0.9.0]: https://github.com/Level/level/releases/tag/0.9.0 + +[0.8.0]: https://github.com/Level/level/releases/tag/0.8.0 diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/LICENSE new file mode 100644 index 0000000..fc29db4 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2013 Rod Vagg and the contributors to level. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/README.md new file mode 100644 index 0000000..b878172 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/README.md @@ -0,0 +1,620 @@ +# level + +**Universal [`abstract-level`](https://github.com/Level/abstract-level) database for Node.js and browsers.** This is a convenience package that exports [`classic-level`](https://github.com/Level/classic-level) in Node.js and [`browser-level`](https://github.com/Level/browser-level) in browsers, making it an ideal entry point to start creating lexicographically sorted key-value databases. + +> :pushpin: Which module should I use? What is `abstract-level`? Head over to the [FAQ](https://github.com/Level/community#faq). + +[![level badge][level-badge]](https://github.com/Level/awesome) +[![npm](https://img.shields.io/npm/v/level.svg)](https://www.npmjs.com/package/level) +[![Node version](https://img.shields.io/node/v/level.svg)](https://www.npmjs.com/package/level) +[![Test](https://img.shields.io/github/actions/workflow/status/Level/level/test.yml?branch=master&label=test)](https://github.com/Level/level/actions/workflows/test.yml) +[![Coverage](https://img.shields.io/codecov/c/github/Level/level?label=\&logo=codecov\&logoColor=fff)](https://codecov.io/gh/Level/level) +[![Standard](https://img.shields.io/badge/standard-informational?logo=javascript\&logoColor=fff)](https://standardjs.com) +[![Common Changelog](https://common-changelog.org/badge.svg)](https://common-changelog.org) +[![Community](https://img.shields.io/badge/community-join-%2370B99E?logo=github)](https://github.com/Level/community/issues) +[![Donate](https://img.shields.io/badge/donate-orange?logo=open-collective\&logoColor=fff)](https://opencollective.com/level) + +## Table of Contents + +
Click to expand + +- [Usage](#usage) +- [Install](#install) +- [Supported Platforms](#supported-platforms) +- [API](#api) + - [`db = new Level(location[, options])`](#db--new-levellocation-options) + - [`db.status`](#dbstatus) + - [`db.open([callback])`](#dbopencallback) + - [`db.close([callback])`](#dbclosecallback) + - [`db.supports`](#dbsupports) + - [`db.get(key[, options][, callback])`](#dbgetkey-options-callback) + - [`db.getMany(keys[, options][, callback])`](#dbgetmanykeys-options-callback) + - [`db.put(key, value[, options][, callback])`](#dbputkey-value-options-callback) + - [`db.del(key[, options][, callback])`](#dbdelkey-options-callback) + - [`db.batch(operations[, options][, callback])`](#dbbatchoperations-options-callback) + - [`chainedBatch = db.batch()`](#chainedbatch--dbbatch) + - [`iterator = db.iterator([options])`](#iterator--dbiteratoroptions) + - [`keyIterator = db.keys([options])`](#keyiterator--dbkeysoptions) + - [`valueIterator = db.values([options])`](#valueiterator--dbvaluesoptions) + - [`db.clear([options][, callback])`](#dbclearoptions-callback) + - [`sublevel = db.sublevel(name[, options])`](#sublevel--dbsublevelname-options) + - [`chainedBatch`](#chainedbatch) + - [`chainedBatch.put(key, value[, options])`](#chainedbatchputkey-value-options) + - [`chainedBatch.del(key[, options])`](#chainedbatchdelkey-options) + - [`chainedBatch.clear()`](#chainedbatchclear) + - [`chainedBatch.write([options][, callback])`](#chainedbatchwriteoptions-callback) + - [`chainedBatch.close([callback])`](#chainedbatchclosecallback) + - [`chainedBatch.length`](#chainedbatchlength) + - [`chainedBatch.db`](#chainedbatchdb) + - [`iterator`](#iterator) + - [`for await...of iterator`](#for-awaitof-iterator) + - [`iterator.next([callback])`](#iteratornextcallback) + - [`iterator.nextv(size[, options][, callback])`](#iteratornextvsize-options-callback) + - [`iterator.all([options][, callback])`](#iteratoralloptions-callback) + - [`iterator.seek(target[, options])`](#iteratorseektarget-options) + - [`iterator.close([callback])`](#iteratorclosecallback) + - [`iterator.db`](#iteratordb) + - [`iterator.count`](#iteratorcount) + - [`iterator.limit`](#iteratorlimit) + - [`keyIterator`](#keyiterator) + - [`valueIterator`](#valueiterator) + - [`sublevel`](#sublevel) + - [`sublevel.prefix`](#sublevelprefix) + - [`sublevel.db`](#subleveldb) +- [Contributing](#contributing) +- [Donate](#donate) +- [License](#license) + +
+ +## Usage + +_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ + +```js +const { Level } = require('level') + +// Create a database +const db = new Level('example', { valueEncoding: 'json' }) + +// Add an entry with key 'a' and value 1 +await db.put('a', 1) + +// Add multiple entries +await db.batch([{ type: 'put', key: 'b', value: 2 }]) + +// Get value of key 'a': 1 +const value = await db.get('a') + +// Iterate entries with keys that are greater than 'a' +for await (const [key, value] of db.iterator({ gt: 'a' })) { + console.log(value) // 2 +} +``` + +All asynchronous methods also support callbacks. + +
Callback example + +```js +db.put('a', { x: 123 }, function (err) { + if (err) throw err + + db.get('a', function (err, value) { + console.log(value) // { x: 123 } + }) +}) +``` + +
+ +TypeScript type declarations are included and cover the methods that are common between `classic-level` and `browser-level`. Usage from TypeScript requires generic type parameters. + +
TypeScript example + +```ts +// Specify types of keys and values (any, in the case of json). +// The generic type parameters default to Level. +const db = new Level('./db', { valueEncoding: 'json' }) + +// All relevant methods then use those types +await db.put('a', { x: 123 }) + +// Specify different types when overriding encoding per operation +await db.get('a', { valueEncoding: 'utf8' }) + +// Though in some cases TypeScript can infer them +await db.get('a', { valueEncoding: db.valueEncoding('utf8') }) + +// It works the same for sublevels +const abc = db.sublevel('abc') +const xyz = db.sublevel('xyz', { valueEncoding: 'json' }) +``` + +
+ +## Install + +With [npm](https://npmjs.org) do: + +```bash +npm install level +``` + +For use in browsers, this package is best used with [`browserify`](https://github.com/browserify/browserify), [`webpack`](https://webpack.js.org/), [`rollup`](https://rollupjs.org/) or similar bundlers. For a quick start, visit [`browserify-starter`](https://github.com/Level/browserify-starter) or [`webpack-starter`](https://github.com/Level/webpack-starter). + +## Supported Platforms + +At the time of writing, `level` works in Node.js 12+ and Electron 5+ on Linux, Mac OS, Windows and FreeBSD, including any future Node.js and Electron release thanks to [Node-API](https://nodejs.org/api/n-api.html), including ARM platforms like Raspberry Pi and Android, as well as in Chrome, Firefox, Edge, Safari, iOS Safari and Chrome for Android. For details, see [Supported Platforms](https://github.com/Level/classic-level#supported-platforms) of `classic-level` and [Browser Support](https://github.com/Level/browser-level#browser-support) of `browser-level`. + +Binary keys and values are supported across the board. + +## API + +The API of `level` follows that of [`abstract-level`](https://github.com/Level/abstract-level). The documentation below covers it all except for [Encodings](https://github.com/Level/abstract-level#encodings), [Events](https://github.com/Level/abstract-level#events) and [Errors](https://github.com/Level/abstract-level#errors) which are exclusively documented in `abstract-level`. For options and additional methods specific to [`classic-level`](https://github.com/Level/classic-level) and [`browser-level`](https://github.com/Level/browser-level), please see their respective READMEs. + +An `abstract-level` and thus `level` database is at its core a [key-value database](https://en.wikipedia.org/wiki/Key%E2%80%93value_database). A key-value pair is referred to as an _entry_ here and typically returned as an array, comparable to [`Object.entries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries). + +### `db = new Level(location[, options])` + +Create a new database or open an existing database. The `location` argument must be a directory path (relative or absolute) where LevelDB will store its files, or in browsers, the name of the [`IDBDatabase`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase) to be opened. + +The optional `options` object may contain: + +- `keyEncoding` (string or object, default `'utf8'`): encoding to use for keys +- `valueEncoding` (string or object, default `'utf8'`): encoding to use for values. + +See [Encodings](https://github.com/Level/abstract-level#encodings) for a full description of these options. Other `options` (except `passive`) are forwarded to `db.open()` which is automatically called in a next tick after the constructor returns. Any read & write operations are queued internally until the database has finished opening. If opening fails, those queued operations will yield errors. + +### `db.status` + +Read-only getter that returns a string reflecting the current state of the database: + +- `'opening'` - waiting for the database to be opened +- `'open'` - successfully opened the database +- `'closing'` - waiting for the database to be closed +- `'closed'` - successfully closed the database. + +### `db.open([callback])` + +Open the database. The `callback` function will be called with no arguments when successfully opened, or with a single error argument if opening failed. If no callback is provided, a promise is returned. Options passed to `open()` take precedence over options passed to the database constructor. The `createIfMissing` and `errorIfExists` options are not supported by [`browser-level`](https://github.com/Level/browser-level). + +The optional `options` object may contain: + +- `createIfMissing` (boolean, default: `true`): If `true`, create an empty database if one doesn't already exist. If `false` and the database doesn't exist, opening will fail. +- `errorIfExists` (boolean, default: `false`): If `true` and the database already exists, opening will fail. +- `passive` (boolean, default: `false`): Wait for, but do not initiate, opening of the database. + +It's generally not necessary to call `open()` because it's automatically called by the database constructor. It may however be useful to capture an error from failure to open, that would otherwise not surface until another method like `db.get()` is called. It's also possible to reopen the database after it has been closed with [`close()`](#dbclosecallback). Once `open()` has then been called, any read & write operations will again be queued internally until opening has finished. + +The `open()` and `close()` methods are idempotent. If the database is already open, the `callback` will be called in a next tick. If opening is already in progress, the `callback` will be called when that has finished. If closing is in progress, the database will be reopened once closing has finished. Likewise, if `close()` is called after `open()`, the database will be closed once opening has finished and the prior `open()` call will receive an error. + +### `db.close([callback])` + +Close the database. The `callback` function will be called with no arguments if closing succeeded or with a single `error` argument if closing failed. If no callback is provided, a promise is returned. + +A database may have associated resources like file handles and locks. When the database is no longer needed (for the remainder of a program) it's recommended to call `db.close()` to free up resources. + +After `db.close()` has been called, no further read & write operations are allowed unless and until `db.open()` is called again. For example, `db.get(key)` will yield an error with code [`LEVEL_DATABASE_NOT_OPEN`](https://github.com/Level/abstract-level#errors). Any unclosed iterators or chained batches will be closed by `db.close()` and can then no longer be used even when `db.open()` is called again. + +### `db.supports` + +A [manifest](https://github.com/Level/supports) describing the features supported by this database. Might be used like so: + +```js +if (!db.supports.permanence) { + throw new Error('Persistent storage is required') +} +``` + +### `db.get(key[, options][, callback])` + +Get a value from the database by `key`. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. +- `valueEncoding`: custom value encoding for this operation, used to decode the value. + +The `callback` function will be called with an error if the operation failed. If the key was not found, the error will have code [`LEVEL_NOT_FOUND`](https://github.com/Level/abstract-level#errors). If successful the first argument will be `null` and the second argument will be the value. If no callback is provided, a promise is returned. + +### `db.getMany(keys[, options][, callback])` + +Get multiple values from the database by an array of `keys`. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `keys`. +- `valueEncoding`: custom value encoding for this operation, used to decode values. + +The `callback` function will be called with an error if the operation failed. If successful the first argument will be `null` and the second argument will be an array of values with the same order as `keys`. If a key was not found, the relevant value will be `undefined`. If no callback is provided, a promise is returned. + +### `db.put(key, value[, options][, callback])` + +Add a new entry or overwrite an existing entry. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. +- `valueEncoding`: custom value encoding for this operation, used to encode the `value`. + +The `callback` function will be called with no arguments if the operation was successful or with an error if it failed. If no callback is provided, a promise is returned. + +### `db.del(key[, options][, callback])` + +Delete an entry by `key`. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. + +The `callback` function will be called with no arguments if the operation was successful or with an error if it failed. If no callback is provided, a promise is returned. + +### `db.batch(operations[, options][, callback])` + +Perform multiple _put_ and/or _del_ operations in bulk. The `operations` argument must be an array containing a list of operations to be executed sequentially, although as a whole they are performed as an atomic operation. + +Each operation must be an object with at least a `type` property set to either `'put'` or `'del'`. If the `type` is `'put'`, the operation must have `key` and `value` properties. It may optionally have `keyEncoding` and / or `valueEncoding` properties to encode keys or values with a custom encoding for just that operation. If the `type` is `'del'`, the operation must have a `key` property and may optionally have a `keyEncoding` property. + +An operation of either type may also have a `sublevel` property, to prefix the key of the operation with the prefix of that sublevel. This allows atomically committing data to multiple sublevels. Keys and values will be encoded by the sublevel, to the same effect as a `sublevel.batch(..)` call. In the following example, the first `value` will be encoded with `'json'` rather than the default encoding of `db`: + +```js +const people = db.sublevel('people', { valueEncoding: 'json' }) +const nameIndex = db.sublevel('names') + +await db.batch([{ + type: 'put', + sublevel: people, + key: '123', + value: { + name: 'Alice' + } +}, { + type: 'put', + sublevel: nameIndex, + key: 'Alice', + value: '123' +}]) +``` + +The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this batch, used to encode keys. +- `valueEncoding`: custom value encoding for this batch, used to encode values. + +Encoding properties on individual operations take precedence. In the following example, the first value will be encoded with the `'utf8'` encoding and the second with `'json'`. + +```js +await db.batch([ + { type: 'put', key: 'a', value: 'foo' }, + { type: 'put', key: 'b', value: 123, valueEncoding: 'json' } +], { valueEncoding: 'utf8' }) +``` + +The `callback` function will be called with no arguments if the batch was successful or with an error if it failed. If no callback is provided, a promise is returned. + +### `chainedBatch = db.batch()` + +Create a [chained batch](#chainedbatch), when `batch()` is called with zero arguments. A chained batch can be used to build and eventually commit an atomic batch of operations. Depending on how it's used, it is possible to obtain greater performance with this form of `batch()`. On `browser-level` however, it is just sugar. + +```js +await db.batch() + .del('bob') + .put('alice', 361) + .put('kim', 220) + .write() +``` + +### `iterator = db.iterator([options])` + +Create an [iterator](#iterator). The optional `options` object may contain the following _range options_ to control the range of entries to be iterated: + +- `gt` (greater than) or `gte` (greater than or equal): define the lower bound of the range to be iterated. Only entries where the key is greater than (or equal to) this option will be included in the range. When `reverse` is true the order will be reversed, but the entries iterated will be the same. +- `lt` (less than) or `lte` (less than or equal): define the higher bound of the range to be iterated. Only entries where the key is less than (or equal to) this option will be included in the range. When `reverse` is true the order will be reversed, but the entries iterated will be the same. +- `reverse` (boolean, default: `false`): iterate entries in reverse order. Beware that a reverse seek can be slower than a forward seek. +- `limit` (number, default: `Infinity`): limit the number of entries yielded. This number represents a _maximum_ number of entries and will not be reached if the end of the range is reached first. A value of `Infinity` or `-1` means there is no limit. When `reverse` is true the entries with the highest keys will be returned instead of the lowest keys. + +The `gte` and `lte` range options take precedence over `gt` and `lt` respectively. If no range options are provided, the iterator will visit all entries of the database, starting at the lowest key and ending at the highest key (unless `reverse` is true). In addition to range options, the `options` object may contain: + +- `keys` (boolean, default: `true`): whether to return the key of each entry. If set to `false`, the iterator will yield keys that are `undefined`. Prefer to use `db.keys()` instead. +- `values` (boolean, default: `true`): whether to return the value of each entry. If set to `false`, the iterator will yield values that are `undefined`. Prefer to use `db.values()` instead. +- `keyEncoding`: custom key encoding for this iterator, used to encode range options, to encode `seek()` targets and to decode keys. +- `valueEncoding`: custom value encoding for this iterator, used to decode values. + +> :pushpin: To instead consume data using streams, see [`level-read-stream`](https://github.com/Level/read-stream) and [`level-web-stream`](https://github.com/Level/web-stream). + +### `keyIterator = db.keys([options])` + +Create a [key iterator](#keyiterator), having the same interface as `db.iterator()` except that it yields keys instead of entries. If only keys are needed, using `db.keys()` may increase performance because values won't have to fetched, copied or decoded. Options are the same as for `db.iterator()` except that `db.keys()` does not take `keys`, `values` and `valueEncoding` options. + +```js +// Iterate lazily +for await (const key of db.keys({ gt: 'a' })) { + console.log(key) +} + +// Get all at once. Setting a limit is recommended. +const keys = await db.keys({ gt: 'a', limit: 10 }).all() +``` + +### `valueIterator = db.values([options])` + +Create a [value iterator](#valueiterator), having the same interface as `db.iterator()` except that it yields values instead of entries. If only values are needed, using `db.values()` may increase performance because keys won't have to fetched, copied or decoded. Options are the same as for `db.iterator()` except that `db.values()` does not take `keys` and `values` options. Note that it _does_ take a `keyEncoding` option, relevant for the encoding of range options. + +```js +// Iterate lazily +for await (const value of db.values({ gt: 'a' })) { + console.log(value) +} + +// Get all at once. Setting a limit is recommended. +const values = await db.values({ gt: 'a', limit: 10 }).all() +``` + +### `db.clear([options][, callback])` + +Delete all entries or a range. Not guaranteed to be atomic. Accepts the following options (with the same rules as on iterators): + +- `gt` (greater than) or `gte` (greater than or equal): define the lower bound of the range to be deleted. Only entries where the key is greater than (or equal to) this option will be included in the range. When `reverse` is true the order will be reversed, but the entries deleted will be the same. +- `lt` (less than) or `lte` (less than or equal): define the higher bound of the range to be deleted. Only entries where the key is less than (or equal to) this option will be included in the range. When `reverse` is true the order will be reversed, but the entries deleted will be the same. +- `reverse` (boolean, default: `false`): delete entries in reverse order. Only effective in combination with `limit`, to delete the last N entries. +- `limit` (number, default: `Infinity`): limit the number of entries to be deleted. This number represents a _maximum_ number of entries and will not be reached if the end of the range is reached first. A value of `Infinity` or `-1` means there is no limit. When `reverse` is true the entries with the highest keys will be deleted instead of the lowest keys. +- `keyEncoding`: custom key encoding for this operation, used to encode range options. + +The `gte` and `lte` range options take precedence over `gt` and `lt` respectively. If no options are provided, all entries will be deleted. The `callback` function will be called with no arguments if the operation was successful or with an error if it failed. If no callback is provided, a promise is returned. + +### `sublevel = db.sublevel(name[, options])` + +Create a [sublevel](#sublevel) that has the same interface as `db` (except for additional methods specific to `classic-level` or `browser-level`) and prefixes the keys of operations before passing them on to `db`. The `name` argument is required and must be a string. + +```js +const example = db.sublevel('example') + +await example.put('hello', 'world') +await db.put('a', '1') + +// Prints ['hello', 'world'] +for await (const [key, value] of example.iterator()) { + console.log([key, value]) +} +``` + +Sublevels effectively separate a database into sections. Think SQL tables, but evented, ranged and real-time! Each sublevel is an `AbstractLevel` instance with its own keyspace, [events](https://github.com/Level/abstract-level#events) and [encodings](https://github.com/Level/abstract-level#encodings). For example, it's possible to have one sublevel with `'buffer'` keys and another with `'utf8'` keys. The same goes for values. Like so: + +```js +db.sublevel('one', { valueEncoding: 'json' }) +db.sublevel('two', { keyEncoding: 'buffer' }) +``` + +An own keyspace means that `sublevel.iterator()` only includes entries of that sublevel, `sublevel.clear()` will only delete entries of that sublevel, and so forth. Range options get prefixed too. + +Fully qualified keys (as seen from the parent database) take the form of `prefix + key` where `prefix` is `separator + name + separator`. If `name` is empty, the effective prefix is two separators. Sublevels can be nested: if `db` is itself a sublevel then the effective prefix is a combined prefix, e.g. `'!one!!two!'`. Note that a parent database will see its own keys as well as keys of any nested sublevels: + +```js +// Prints ['!example!hello', 'world'] and ['a', '1'] +for await (const [key, value] of db.iterator()) { + console.log([key, value]) +} +``` + +> :pushpin: The key structure is equal to that of [`subleveldown`](https://github.com/Level/subleveldown) which offered sublevels before they were built-in to `abstract-level`. This means that an `abstract-level` sublevel can read sublevels previously created with (and populated by) `subleveldown`. + +Internally, sublevels operate on keys that are either a string, Buffer or Uint8Array, depending on parent database and choice of encoding. Which is to say: binary keys are fully supported. The `name` must however always be a string and can only contain ASCII characters. + +The optional `options` object may contain: + +- `separator` (string, default: `'!'`): Character for separating sublevel names from user keys and each other. Must sort before characters used in `name`. An error will be thrown if that's not the case. +- `keyEncoding` (string or object, default `'utf8'`): encoding to use for keys +- `valueEncoding` (string or object, default `'utf8'`): encoding to use for values. + +The `keyEncoding` and `valueEncoding` options are forwarded to the `AbstractLevel` constructor and work the same, as if a new, separate database was created. They default to `'utf8'` regardless of the encodings configured on `db`. Other options are forwarded too but `abstract-level` (and therefor `level`) has no relevant options at the time of writing. For example, setting the `createIfMissing` option will have no effect. Why is that? + +Like regular databases, sublevels open themselves but they do not affect the state of the parent database. This means a sublevel can be individually closed and (re)opened. If the sublevel is created while the parent database is opening, it will wait for that to finish. If the parent database is closed, then opening the sublevel will fail and subsequent operations on the sublevel will yield errors with code [`LEVEL_DATABASE_NOT_OPEN`](https://github.com/Level/abstract-level#errors). + +### `chainedBatch` + +#### `chainedBatch.put(key, value[, options])` + +Queue a `put` operation on this batch, not committed until `write()` is called. This will throw a [`LEVEL_INVALID_KEY`](https://github.com/Level/abstract-level#errors) or [`LEVEL_INVALID_VALUE`](https://github.com/Level/abstract-level#errors) error if `key` or `value` is invalid. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. +- `valueEncoding`: custom value encoding for this operation, used to encode the `value`. +- `sublevel` (sublevel instance): act as though the `put` operation is performed on the given sublevel, to similar effect as `sublevel.batch().put(key, value)`. This allows atomically committing data to multiple sublevels. The `key` will be prefixed with the `prefix` of the sublevel, and the `key` and `value` will be encoded by the sublevel (using the default encodings of the sublevel unless `keyEncoding` and / or `valueEncoding` are provided). + +#### `chainedBatch.del(key[, options])` + +Queue a `del` operation on this batch, not committed until `write()` is called. This will throw a [`LEVEL_INVALID_KEY`](https://github.com/Level/abstract-level#errors) error if `key` is invalid. The optional `options` object may contain: + +- `keyEncoding`: custom key encoding for this operation, used to encode the `key`. +- `sublevel` (sublevel instance): act as though the `del` operation is performed on the given sublevel, to similar effect as `sublevel.batch().del(key)`. This allows atomically committing data to multiple sublevels. The `key` will be prefixed with the `prefix` of the sublevel, and the `key` will be encoded by the sublevel (using the default key encoding of the sublevel unless `keyEncoding` is provided). + +#### `chainedBatch.clear()` + +Clear all queued operations on this batch. + +#### `chainedBatch.write([options][, callback])` + +Commit the queued operations for this batch. All operations will be written atomically, that is, they will either all succeed or fail with no partial commits. + +There are no `options` (that are common between `classic-level` and `browser-level`). Note that `write()` does not take encoding options. Those can only be set on `put()` and `del()`. + +The `callback` function will be called with no arguments if the batch was successful or with an error if it failed. If no callback is provided, a promise is returned. + +After `write()` or `close()` has been called, no further operations are allowed. + +#### `chainedBatch.close([callback])` + +Free up underlying resources. This should be done even if the chained batch has zero queued operations. Automatically called by `write()` so normally not necessary to call, unless the intent is to discard a chained batch without committing it. The `callback` function will be called with no arguments. If no callback is provided, a promise is returned. Closing the batch is an idempotent operation, such that calling `close()` more than once is allowed and makes no difference. + +#### `chainedBatch.length` + +The number of queued operations on the current batch. + +#### `chainedBatch.db` + +A reference to the database that created this chained batch. + +### `iterator` + +An iterator allows one to lazily read a range of entries stored in the database. The entries will be sorted by keys in [lexicographic order](https://en.wikipedia.org/wiki/Lexicographic_order) (in other words: byte order) which in short means key `'a'` comes before `'b'` and key `'10'` comes before `'2'`. + +A `classic-level` iterator reads from a snapshot of the database, created at the time `db.iterator()` was called. This means the iterator will not see the data of simultaneous write operations. A `browser-level` iterator does not offer such guarantees, as is indicated by `db.supports.snapshots`. That property will be true in Node.js and false in browsers. + +Iterators can be consumed with [`for await...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) and `iterator.all()`, or by manually calling `iterator.next()` or `nextv()` in succession. In the latter case, `iterator.close()` must always be called. In contrast, finishing, throwing, breaking or returning from a `for await...of` loop automatically calls `iterator.close()`, as does `iterator.all()`. + +An iterator reaches its natural end in the following situations: + +- The end of the database has been reached +- The end of the range has been reached +- The last `iterator.seek()` was out of range. + +An iterator keeps track of calls that are in progress. It doesn't allow concurrent `next()`, `nextv()` or `all()` calls (including a combination thereof) and will throw an error with code [`LEVEL_ITERATOR_BUSY`](https://github.com/Level/abstract-level#errors) if that happens: + +```js +// Not awaited and no callback provided +iterator.next() + +try { + // Which means next() is still in progress here + iterator.all() +} catch (err) { + console.log(err.code) // 'LEVEL_ITERATOR_BUSY' +} +``` + +#### `for await...of iterator` + +Yields entries, which are arrays containing a `key` and `value`. The type of `key` and `value` depends on the options passed to `db.iterator()`. + +```js +try { + for await (const [key, value] of db.iterator()) { + console.log(key) + } +} catch (err) { + console.error(err) +} +``` + +#### `iterator.next([callback])` + +Advance to the next entry and yield that entry. If an error occurs, the `callback` function will be called with an error. Otherwise, the `callback` receives `null`, a `key` and a `value`. The type of `key` and `value` depends on the options passed to `db.iterator()`. If the iterator has reached its natural end, both `key` and `value` will be `undefined`. + +If no callback is provided, a promise is returned for either an entry array (containing a `key` and `value`) or `undefined` if the iterator reached its natural end. + +**Note:** `iterator.close()` must always be called once there's no intention to call `next()` or `nextv()` again. Even if such calls yielded an error and even if the iterator reached its natural end. Not closing the iterator will result in memory leaks and may also affect performance of other operations if many iterators are unclosed and each is holding a snapshot of the database. + +#### `iterator.nextv(size[, options][, callback])` + +Advance repeatedly and get at most `size` amount of entries in a single call. Can be faster than repeated `next()` calls. The `size` argument must be an integer and has a soft minimum of 1. There are no `options` at the moment. + +If an error occurs, the `callback` function will be called with an error. Otherwise, the `callback` receives `null` and an array of entries, where each entry is an array containing a key and value. The natural end of the iterator will be signaled by yielding an empty array. If no callback is provided, a promise is returned. + +```js +const iterator = db.iterator() + +while (true) { + const entries = await iterator.nextv(100) + + if (entries.length === 0) { + break + } + + for (const [key, value] of entries) { + // .. + } +} + +await iterator.close() +``` + +#### `iterator.all([options][, callback])` + +Advance repeatedly and get all (remaining) entries as an array, automatically closing the iterator. Assumes that those entries fit in memory. If that's not the case, instead use `next()`, `nextv()` or `for await...of`. There are no `options` at the moment. If an error occurs, the `callback` function will be called with an error. Otherwise, the `callback` receives `null` and an array of entries, where each entry is an array containing a key and value. If no callback is provided, a promise is returned. + +```js +const entries = await db.iterator({ limit: 100 }).all() + +for (const [key, value] of entries) { + // .. +} +``` + +#### `iterator.seek(target[, options])` + +Seek to the key closest to `target`. Subsequent calls to `iterator.next()`, `nextv()` or `all()` (including implicit calls in a `for await...of` loop) will yield entries with keys equal to or larger than `target`, or equal to or smaller than `target` if the `reverse` option passed to `db.iterator()` was true. + +The optional `options` object may contain: + +- `keyEncoding`: custom key encoding, used to encode the `target`. By default the `keyEncoding` option of the iterator is used or (if that wasn't set) the `keyEncoding` of the database. + +If range options like `gt` were passed to `db.iterator()` and `target` does not fall within that range, the iterator will reach its natural end. + +#### `iterator.close([callback])` + +Free up underlying resources. The `callback` function will be called with no arguments. If no callback is provided, a promise is returned. Closing the iterator is an idempotent operation, such that calling `close()` more than once is allowed and makes no difference. + +If a `next()` ,`nextv()` or `all()` call is in progress, closing will wait for that to finish. After `close()` has been called, further calls to `next()` ,`nextv()` or `all()` will yield an error with code [`LEVEL_ITERATOR_NOT_OPEN`](https://github.com/Level/abstract-level#errors). + +#### `iterator.db` + +A reference to the database that created this iterator. + +#### `iterator.count` + +Read-only getter that indicates how many keys have been yielded so far (by any method) excluding calls that errored or yielded `undefined`. + +#### `iterator.limit` + +Read-only getter that reflects the `limit` that was set in options. Greater than or equal to zero. Equals `Infinity` if no limit, which allows for easy math: + +```js +const hasMore = iterator.count < iterator.limit +const remaining = iterator.limit - iterator.count +``` + +### `keyIterator` + +A key iterator has the same interface as `iterator` except that its methods yield keys instead of entries. For the `keyIterator.next(callback)` method, this means that the `callback` will receive two arguments (an error and key) instead of three. Usage is otherwise the same. + +### `valueIterator` + +A value iterator has the same interface as `iterator` except that its methods yield values instead of entries. For the `valueIterator.next(callback)` method, this means that the `callback` will receive two arguments (an error and value) instead of three. Usage is otherwise the same. + +### `sublevel` + +A sublevel is an instance of the `AbstractSublevel` class, which extends `AbstractLevel` and thus has the same API as documented above. Sublevels have a few additional properties. + +#### `sublevel.prefix` + +Prefix of the sublevel. A read-only string property. + +```js +const example = db.sublevel('example') +const nested = example.sublevel('nested') + +console.log(example.prefix) // '!example!' +console.log(nested.prefix) // '!example!!nested!' +``` + +#### `sublevel.db` + +Parent database. A read-only property. + +```js +const example = db.sublevel('example') +const nested = example.sublevel('nested') + +console.log(example.db === db) // true +console.log(nested.db === db) // true +``` + +## Contributing + +[`Level/level`](https://github.com/Level/level) is an **OPEN Open Source Project**. This means that: + +> Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project. + +See the [Contribution Guide](https://github.com/Level/community/blob/master/CONTRIBUTING.md) for more details. + +## Donate + +Support us with a monthly donation on [Open Collective](https://opencollective.com/level) and help us continue our work. + +## License + +[MIT](LICENSE) + +[level-badge]: https://leveljs.org/img/badge.svg diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/UPGRADING.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/UPGRADING.md new file mode 100644 index 0000000..12fe215 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/UPGRADING.md @@ -0,0 +1,397 @@ +# Upgrade Guide + +This document describes breaking changes and how to upgrade. For a complete list of changes including minor and patch releases, please refer to the [changelog](CHANGELOG.md). + +## 8.0.0 + +**This release replaces `leveldown` and `level-js` with [`classic-level`](https://github.com/Level/classic-level) and [`browser-level`](https://github.com/Level/browser-level). These modules implement the [`abstract-level`](https://github.com/Level/abstract-level) interface instead of [`abstract-leveldown`](https://github.com/Level/abstract-leveldown). This gives them the same API as `level@7` without having to be wrapped with [`levelup`](https://github.com/Level/levelup) or [`encoding-down`](https://github.com/Level/encoding-down). In addition, you can now choose to use Uint8Array instead of Buffer. Sublevels are built-in.** + +We've put together several upgrade guides for different modules. See the [FAQ](https://github.com/Level/community#faq) to find the best upgrade guide for you. This one describes how to upgrade `level`. + +Support of Node.js 10 has been dropped. + +### Changes to initialization + +We started using classes, which means using `new` is now required. If you previously did: + +```js +const level = require('level') +const db = level('db') +``` + +You must now do: + +```js +const { Level } = require('level') +const db = new Level('db') +``` + +### TypeScript makes a win + +TypeScript type declarations are now included in the npm package(s). For `level` it's an intersection of `classic-level` and `browser-level` types that includes their options but excludes methods like `compactRange()` that can only be found in either. JavaScript folks using VSCode will also benefit from the new types because they enable auto-completion and now include documentation. + +### Waking up from limbo + +Deferred open - meaning that a database opens itself and any operations made in the mean time are queued up in memory - remains built-in. A new behavior is that those operations will yield errors if opening failed. They'd previously end up in limbo. + +An `abstract-level` and thus `level` database is not "patch-safe". If some form of plugin monkey-patches a database method, it must now also take the responsibility of deferring the operation (as well as handling promises and callbacks) using [`db.defer()`](https://github.com/Level/abstract-level#dbdeferfn). + +### Creating the location recursively + +To align behavior between platforms, `classic-level` and therefore `level@8` creates the location directory recursively. While `leveldown` and therefore `level@7` would only do so on Windows. In the following example, the `foo` directory does not have to exist beforehand: + +```js +const db = new Level('foo/bar') +``` + +This new behavior may break expectations, given typical filesystem behavior, or it could be a convenient feature, if the database is considered to abstract away the filesystem. We're [collecting feedback](https://github.com/Level/classic-level/issues/7) to determine what to do in a next (major) version. Your vote is most welcome! + +### No constructor callback + +The database constructor no longer takes a callback argument. Instead call `db.open()` if you wish to wait for opening (which is not necessary to use the database) or to capture an error. If that's your reason for using the callback and you previously initialized a database like so: + +```js +level('fruits', function (err, db) { + // .. +}) +``` + +You must now do one of: + +```js +db.open(callback) +await db.open() +``` + +### There is only encodings + +Encodings have a new home in `abstract-level` and are now powered by [`level-transcoder`](https://github.com/Level/transcoder). The main change is that logic from the existing public API has been expanded down into the storage layer. There are however a few differences from `level@7`. Some breaking: + +- The lesser-used `'id'`, `'ascii'`, `'ucs2'` and `'utf16le'` encodings are not supported +- The undocumented `encoding` option (as an alias for `valueEncoding`) is not supported. + +And some non-breaking: + +- The `'binary'` encoding has been renamed to `'buffer'`, with `'binary'` as an alias +- The `'utf8'` encoding previously did not touch Buffers. Now it will call `buffer.toString('utf8')` for consistency. Consumers can use the `'buffer'` encoding to avoid this conversion. + +Both `classic-level` and `browser-level` support Uint8Array data, in addition to Buffer. It's a separate encoding called `'view'` that can be used interchangeably: + +```js +const db = new Level('people', { valueEncoding: 'view' }) + +await db.put('elena', new Uint8Array([97, 98, 99])) +await db.get('elena') // Uint8Array +await db.get('elena', { valueEncoding: 'utf8' }) // 'abc' +await db.get('elena', { valueEncoding: 'buffer' }) // Buffer +``` + +For browsers you can choose to use Uint8Array exclusively and omit the [`buffer`](https://github.com/feross/buffer) shim from your JavaScript bundle (through configuration of Webpack, Browserify or other). + +### Streams have moved + +Node.js readable streams must now be created with a new standalone module called [`level-read-stream`](https://github.com/Level/read-stream) rather than database methods like `db.createReadStream()`. For browsers you might prefer [`level-web-stream`](https://github.com/Level/web-stream) which does not require bundling the [`buffer`](https://github.com/feross/buffer) or [`readable-stream`](https://github.com/nodejs/readable-stream) shims. Both `level-read-stream` and `level-web-stream` can be used in Node.js and browsers. The former is significantly faster (also compared to `level@7`, thanks to a new `nextv()` method on iterators). The latter is a step towards a standard library for JavaScript across Node.js, Deno and browsers. + +To offer an alternative to `db.createKeyStream()` and `db.createValueStream()`, two new types of iterators have been added: `db.keys()` and `db.values()`. + +### State checks for safety + +On any operation, an `abstract-level` and thus `level` database checks if it's open. If not, it will either throw an error (if the relevant API is synchronous) or asynchronously yield an error. For example: + +```js +await db.close() + +try { + db.iterator() +} catch (err) { + console.log(err.code) // LEVEL_DATABASE_NOT_OPEN +} +``` + +_Errors now have a `code` property. More on that below\._ + +### Zero-length keys and range options are now valid + +These keys sort before anything else. Historically they weren't supported for causing segmentation faults in `leveldown`. That doesn't apply to today's codebase. You can now do: + +```js +await db.put('', 'abc') + +console.log(await db.get('')) // 'abc' +console.log(await db.get(new Uint8Array(0), { keyEncoding: 'view' })) // 'abc' + +for await (const [key, value] of db.iterator({ lte: '' })) { + console.log(value) // 'abc' +} +``` + +### It doesn't end there + +The `iterator.end()` method has been renamed to `iterator.close()`, with `end()` being an alias until a next major version. The term "close" makes it easier to differentiate between the iterator having reached its natural end (data-wise) versus closing it to cleanup resources. If you previously did: + +```js +const iterator = db.iterator() +iterator.end(callback) +``` + +You should now do one of: + +```js +iterator.close(callback) +await iterator.close() +``` + +On `db.close()`, non-closed iterators are now automatically closed (only for safety reasons). If a call like `next()` is in progress, closing the iterator or database will wait for that. Calling `iterator.close()` more than once is now allowed and makes no difference. + +### Other changes to iterators + +- In browsers, backpressure is now preferred over snapshot guarantees. For details, please see [`browser-level@1`](https://github.com/Level/browser-level/blob/main/UPGRADING.md#100). On the flip side, `iterator.seek()` now also works in browsers. +- Use of [`level-concat-iterator`](https://github.com/Level/concat-iterator) can be replaced with [`iterator.all()`](https://github.com/Level/level#iteratoralloptions-callback). The former does support `abstract-level` databases but the latter is optimized and always has snapshot guarantees. +- The previously undocumented `highWaterMark` option of `leveldown` is called [`highWaterMarkBytes`](https://github.com/Level/classic-level#about-high-water) in `classic-level` to remove a conflict with streams. +- On iterators with `{ keys: false }` or `{ values: false }` options, the yielded key or value is now consistently `undefined`. + +### A chained batch should be closed + +Chained batch has a new method `close()` which is an idempotent operation and automatically called after `write()` (for backwards compatibility) or on `db.close()`. This to ensure batches can't be used after closing and reopening a db. If a `write()` is in progress, closing will wait for that. If `write()` is never called then `close()` must be and that's a breaking change because inaction will cause memory leaks. For example: + +```js +const batch = db.batch() + .put('elena', 'abc') + .del('steve') + +if (someCondition) { + await batch.write() +} else { + // Decided not to commit + await batch.close() +} + +// In either case this will throw +batch.put('daniel', 'xyz') +``` + +### Errors now use codes + +The [`level-errors`](https://github.com/Level/errors) module is no longer used or exposed by `level@8`. Instead errors thrown or yielded from a database [have a `code` property](https://github.com/Level/abstract-level#errors). Going forward, the semver contract will be on `code` and error messages will change without a semver-major bump. + +To minimize breakage, the most used error as yielded by `get()` when an entry is not found, has the same properties that `level-errors` added (`notFound` and `status`) in addition to code `LEVEL_NOT_FOUND`. Those properties will be removed in a future version. If you previously did: + +```js +db.get('abc', function (err, value) { + if (err && err.notFound) { + // Handle missing entry + } +}) +``` + +That will still work but it's preferred to do: + +```js +db.get('abc', function (err, value) { + if (err && err.code === 'LEVEL_NOT_FOUND') { + // Handle missing entry + } +}) +``` + +Or using promises: + +```js +try { + const value = await db.get('abc') +} catch (err) { + if (err.code === 'LEVEL_NOT_FOUND') { + // Handle missing entry + } +} +``` + +Side note: it's been suggested more than once to remove this error altogether and we likely will after the dust has settled on `abstract-level`. + +### Changes to lesser-used properties and methods + +The following properties and methods can no longer be accessed, as they've been removed, renamed or replaced with internal [symbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol). + +| Object | Property or method | Original module | New module | +| :------------ | :------------------------ | :------------------- | :--------------- | +| db | `_setupIteratorOptions()` | `abstract-leveldown` | `abstract-level` | +| db | `prefix` 1 | `level-js` | `browser-level` | +| db | `upgrade()` | `level-js` | `browser-level` | +| iterator | `_nexting` | `abstract-leveldown` | `abstract-level` | +| iterator | `_ended` | `abstract-leveldown` | `abstract-level` | +| iterator | `cache` 2 | `leveldown` | `classic-level` | +| iterator | `finished` | `leveldown` | `classic-level` | +| chained batch | `_written` | `abstract-leveldown` | `abstract-level` | +| chained batch | `_checkWritten()` | `abstract-leveldown` | `abstract-level` | +| chained batch | `_operations` | `abstract-leveldown` | `abstract-level` | + + + +1. Conflicted with the `db.prefix` property of sublevels. Renamed to `db.namePrefix`. +2. If you were using this then you'll want to checkout the new [`nextv()`](https://github.com/Level/level#iteratornextvsize-options-callback) method. + + + +The following properties are now read-only getters. + +| Object | Property | Original module | New module | +| :------------ | :----------------- | :------------------- | :--------------- | +| db | `status` | `abstract-leveldown` | `abstract-level` | +| db | `location` | `leveldown` | `classic-level` | +| db | `location` | `level-js` | `browser-level` | +| db | `namePrefix` | `level-js` | `browser-level` | +| db | `version` | `level-js` | `browser-level` | +| db | `db` (IDBDatabase) | `level-js` | `browser-level` | +| chained batch | `length` | `levelup` | `abstract-level` | + +### Sublevels are built-in + +_This section is only relevant if you use [`subleveldown`](https://github.com/Level/subleveldown), which can not wrap a `level@8` database._ + +If you previously did: + +```js +const sub = require('subleveldown') +const example1 = sub(db, 'example1') +const example2 = sub(db, 'example2', { valueEncoding: 'json' }) +``` + +You must now do: + +```js +const example1 = db.sublevel('example1') +const example2 = db.sublevel('example2', { valueEncoding: 'json' }) +``` + +The key structure is equal to that of `subleveldown`. This means that a sublevel can read sublevels previously created with (and populated by) `subleveldown`. There are some new features: + +- `db.batch(..)` takes a `sublevel` option on operations, to atomically commit data to multiple sublevels +- Sublevels support Uint8Array in addition to Buffer. + +To reduce function overloads, the prefix argument (`example1` above) is now required and it's called `name` here. If you previously did one of the following, resulting in an empty name: + +```js +subleveldown(db) +subleveldown(db, { separator: '@' }) +``` + +You must now use an explicit empty name: + +```js +db.sublevel('') +db.sublevel('', { separator: '@' }) +``` + +The string shorthand for `{ separator }` has also been removed. If you previously did: + +```js +subleveldown(db, 'example', '@') +``` + +You must now do: + +```js +db.sublevel('example', { separator: '@' }) +``` + +Third, the `open` option has been removed. If you need an asynchronous open hook, feel free to open an issue to discuss restoring this API. + +Lastly, the error message `Parent database is not open` (courtesy of `subleveldown` which had to check open state to prevent segmentation faults from underlying databases) changed to error code [`LEVEL_DATABASE_NOT_OPEN`](https://github.com/Level/abstract-level#errors) (courtesy of `abstract-level` which does those checks on any database). + +## 7.0.0 + +Legacy range options have been removed ([Level/community#86](https://github.com/Level/community/issues/86)). If you previously did: + +```js +db.createReadStream({ start: 'a', end: 'z' }) +``` + +An error would now be thrown and you must instead do: + +```js +db.createReadStream({ gte: 'a', lte: 'z' }) +``` + +The same applies to `db.iterator()`, `db.createKeyStream()` and `db.createValueStream()`. + +This release also drops support of legacy runtime environments ([Level/community#98](https://github.com/Level/community/issues/98)): + +- Node.js 6 and 8 +- Internet Explorer 11 +- Safari 9-11 +- Stock Android browser (AOSP). + +Lastly, in browsers, the [`immediate`](https://github.com/calvinmetcalf/immediate) and `process` browser shims for `process.nextTick()` have been replaced with the smaller [`queue-microtask`](https://github.com/feross/queue-microtask), except in streams. In the future we might use `queueMicrotask()` in Node.js too. + +## 6.0.0 + +**No breaking changes to the `level` API. If you're only using `level` in Node.js or Electron, you can upgrade without thinking twice.** + +The major bump is for browsers, because `level` upgraded to [`level-js@5`](https://github.com/Level/level-js): + +> Support of keys & values other than strings and Buffers has been dropped. Internally `level-js` now stores keys & values as binary which solves a number of compatibility issues ([Level/memdown#186](https://github.com/Level/memdown/issues/186)). If you pass in a key or value that isn't a string or Buffer, it will be irreversibly stringified. +> +> Existing IndexedDB databases created with `level-js@4` \[via `level@5`] can be read only if they used binary keys and string or binary values. Other types will come out stringified, and string keys will sort incorrectly. Use the included `upgrade()` utility to convert stored data to binary (in so far the environment supports it): +> +> ```js +> var level = require('level') +> var reachdown = require('reachdown') +> var db = level('my-db') +> +> db.open(function (err) { +> if (err) throw err +> +> reachdown(db, 'level-js').upgrade(function (err) { +> if (err) throw err +> }) +> }) +> ``` + +### New Features :sparkles: + +In case you missed it (a few of these already floated into `level@5`) some exciting new features are now available in all environments: + +- Added [`db.clear()`](https://github.com/Level/level#dbclearoptions-callback) to delete all entries or a range! Also works in [`subleveldown`](https://github.com/Level/subleveldown) - empty that bucket! +- Check out [`db.supports`](https://github.com/Level/level#supports): a manifest describing the features of a db! +- Glorious: `leveldown` ships a prebuilt binary for Linux that is now [compatible with Debian 8, Ubuntu 14.04, RHEL 7, CentOS 7 and other flavors with an old glibc](https://github.com/Level/leveldown/pull/674)! +- With thanks to [Cirrus CI](https://cirrus-ci.org/), `leveldown` is now [continuously tested in FreeBSD](https://github.com/Level/leveldown/pull/678)! + +Go forth and build amazing things. + +## 5.0.0 + +Upgraded to [`leveldown@5.0.0`](https://github.com/Level/leveldown/blob/v5.0.0/UPGRADING.md#v5) and (through `level-packager@5`) [`levelup@4`](https://github.com/Level/levelup/blob/v4.0.0/UPGRADING.md#v4) and [`encoding-down@6`](https://github.com/Level/encoding-down/blob/v6.0.0/UPGRADING.md#v6). Please follow these links for more information. A quick summary: range options (e.g. `gt`) are now serialized the same as keys, `{ gt: undefined }` is not the same as `{}`, nullish values are now rejected and streams are backed by [`readable-stream@3`](https://github.com/nodejs/readable-stream#version-3xx). + +In addition, `level` got browser support! It uses [`leveldown`](https://github.com/Level/leveldown) in node and [`level-js`](https://github.com/Level/level-js) in browsers (backed by [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)). As such, [`level-browserify`](https://github.com/Level/level-browserify) is not needed anymore and will be deprecated later on. To learn what the integration of `level-js` means for platform, browser and type support, please see the updated [README](README.md#supported-platforms). + +## 4.0.0 + +Dropped support for node 4. No other breaking changes. + +## 3.0.0 + +No breaking changes to the `level` API. + +This is an upgrade to `leveldown@^3.0.0` which is based on `abstract-leveldown@~4.0.0` which in turn contains breaking changes to [`.batch()`](https://github.com/Level/abstract-leveldown/commit/a2621ad70571f6ade9d2be42632ece042e068805). Though this is negated by `levelup`, we decided to release a new major version in the event of dependents reaching down into `db.db`. + +## 2.0.0 + +No breaking changes to the `level` API. + +The parts that make up `level` have been refactored to increase modularity. This is an upgrade to `leveldown@~2.0.0` and `level-packager@~2.0.0`, which in turn upgraded to `levelup@^2.0.0`. The responsibility of encoding keys and values moved from [`levelup`](https://github.com/Level/levelup) to [`encoding-down`](https://github.com/Level/encoding-down), which comes bundled with [`level-packager`](https://github.com/Level/packager). + +Being a convenience package, `level` glues the parts back together to form a drop-in replacement for the users of `levelup@1`, while staying fully compatible with `level@1`. One thing we do get for free, is native Promise support. + +```js +const db = level('db') +await db.put('foo', 'bar') +console.log(await db.get('foo')) +``` + +This does not affect the existing callback API, functionality-wise or performance-wise. + +For more information please check the corresponding `CHANGELOG.md` for: + +- [`levelup`](https://github.com/Level/levelup/blob/master/CHANGELOG.md) +- [`leveldown`](https://github.com/Level/leveldown/blob/master/CHANGELOG.md) +- [`level-packager`](https://github.com/Level/packager/blob/master/CHANGELOG.md) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/browser.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/browser.js new file mode 100644 index 0000000..9d2a3c3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/browser.js @@ -0,0 +1 @@ +exports.Level = require('browser-level').BrowserLevel diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/index.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/index.d.ts new file mode 100644 index 0000000..d8e049f --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/index.d.ts @@ -0,0 +1,87 @@ +import * as AbstractLevel from 'abstract-level' +import * as ClassicLevel from 'classic-level' +import * as BrowserLevel from 'browser-level' + +/** + * Universal {@link AbstractLevel} database for Node.js and browsers. + * + * @template KDefault The default type of keys if not overridden on operations. + * @template VDefault The default type of values if not overridden on operations. + */ +export class Level + extends AbstractLevel.AbstractLevel { + /** + * Database constructor. + * + * @param location Directory path (relative or absolute) where LevelDB will store its + * files, or in browsers, the name of the + * [`IDBDatabase`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase) to be + * opened. + * @param options Options, of which some will be forwarded to {@link open}. + */ + constructor (location: string, options?: DatabaseOptions | undefined) + + /** + * Location that was passed to the constructor. + */ + get location (): string + + open (): Promise + open (options: OpenOptions): Promise + open (callback: AbstractLevel.NodeCallback): void + open (options: OpenOptions, callback: AbstractLevel.NodeCallback): void + + get (key: KDefault): Promise + get (key: KDefault, callback: AbstractLevel.NodeCallback): void + get (key: K, options: GetOptions): Promise + get (key: K, options: GetOptions, callback: AbstractLevel.NodeCallback): void + + getMany (keys: KDefault[]): Promise + getMany (keys: KDefault[], callback: AbstractLevel.NodeCallback): void + getMany (keys: K[], options: GetManyOptions): Promise + getMany (keys: K[], options: GetManyOptions, callback: AbstractLevel.NodeCallback): void + + put (key: KDefault, value: VDefault): Promise + put (key: KDefault, value: VDefault, callback: AbstractLevel.NodeCallback): void + put (key: K, value: V, options: PutOptions): Promise + put (key: K, value: V, options: PutOptions, callback: AbstractLevel.NodeCallback): void + + del (key: KDefault): Promise + del (key: KDefault, callback: AbstractLevel.NodeCallback): void + del (key: K, options: DelOptions): Promise + del (key: K, options: DelOptions, callback: AbstractLevel.NodeCallback): void + + batch (operations: Array>): Promise + batch (operations: Array>, callback: AbstractLevel.NodeCallback): void + batch (operations: Array>, options: BatchOptions): Promise + batch (operations: Array>, options: BatchOptions, callback: AbstractLevel.NodeCallback): void + batch (): ChainedBatch + + iterator (): Iterator + iterator (options: IteratorOptions): Iterator + + keys (): KeyIterator + keys (options: KeyIteratorOptions): KeyIterator + + values (): ValueIterator + values (options: ValueIteratorOptions): ValueIterator +} + +export type DatabaseOptions = ClassicLevel.DatabaseOptions & BrowserLevel.DatabaseOptions +export type OpenOptions = ClassicLevel.OpenOptions & BrowserLevel.OpenOptions +export type GetOptions = ClassicLevel.GetOptions & BrowserLevel.GetOptions +export type GetManyOptions = ClassicLevel.GetManyOptions & BrowserLevel.GetManyOptions +export type PutOptions = ClassicLevel.PutOptions & BrowserLevel.PutOptions +export type DelOptions = ClassicLevel.DelOptions & BrowserLevel.DelOptions + +export type BatchOptions = ClassicLevel.BatchOptions & BrowserLevel.BatchOptions +export type BatchOperation = ClassicLevel.BatchOperation & BrowserLevel.BatchOperation +export type ChainedBatch = ClassicLevel.ChainedBatch & BrowserLevel.ChainedBatch + +export type Iterator = ClassicLevel.Iterator & BrowserLevel.Iterator +export type KeyIterator = ClassicLevel.KeyIterator & BrowserLevel.KeyIterator +export type ValueIterator = ClassicLevel.ValueIterator & BrowserLevel.ValueIterator + +export type IteratorOptions = ClassicLevel.IteratorOptions & BrowserLevel.IteratorOptions +export type KeyIteratorOptions = ClassicLevel.KeyIteratorOptions & BrowserLevel.KeyIteratorOptions +export type ValueIteratorOptions = ClassicLevel.ValueIteratorOptions & BrowserLevel.ValueIteratorOptions diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/index.js new file mode 100644 index 0000000..f374cea --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/index.js @@ -0,0 +1 @@ +exports.Level = require('classic-level').ClassicLevel diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/package.json new file mode 100644 index 0000000..7f27490 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/level/package.json @@ -0,0 +1,61 @@ +{ + "name": "level", + "version": "8.0.1", + "description": "Universal abstract-level database for Node.js and browsers", + "license": "MIT", + "main": "index.js", + "types": "./index.d.ts", + "scripts": { + "test": "standard && ts-standard *.ts && nyc node test.js", + "test-browsers-local": "airtap --coverage test.js && nyc report", + "coverage": "nyc report -r lcovonly" + }, + "files": [ + "browser.js", + "index.js", + "index.d.ts", + "CHANGELOG.md", + "UPGRADING.md" + ], + "browser": "browser.js", + "dependencies": { + "abstract-level": "^1.0.4", + "browser-level": "^1.0.1", + "classic-level": "^1.2.0" + }, + "devDependencies": { + "@types/node": "^18.0.0", + "@voxpelli/tsconfig": "^4.0.0", + "airtap": "^4.0.1", + "airtap-playwright": "^1.0.1", + "hallmark": "^4.0.0", + "nyc": "^15.0.0", + "standard": "^16.0.3", + "tape": "^5.0.1", + "ts-standard": "^11.0.0", + "typescript": "^4.5.5", + "uuid": "^9.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/level" + }, + "repository": { + "type": "git", + "url": "https://github.com/Level/level.git" + }, + "homepage": "https://github.com/Level/level", + "keywords": [ + "level", + "leveldb", + "stream", + "database", + "db", + "store", + "storage", + "json" + ], + "engines": { + "node": ">=12" + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/CHANGELOG.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/CHANGELOG.md new file mode 100644 index 0000000..fd64972 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/CHANGELOG.md @@ -0,0 +1,23 @@ +# Changelog + +## [1.0.2] - 2022-02-19 + +### Fixed + +- Align LICENSE format with standardized SPDX ([`915c436`](https://github.com/vweevers/module-error/commit/915c436)). + +## [1.0.1] - 2021-10-24 + +### Fixed + +- Add types ([`027e1ea`](https://github.com/vweevers/module-error/commit/027e1ea)) + +## [1.0.0] - 2021-10-22 + +:seedling: Initial release. + +[1.0.2]: https://github.com/vweevers/module-error/releases/tag/v1.0.2 + +[1.0.1]: https://github.com/vweevers/module-error/releases/tag/v1.0.1 + +[1.0.0]: https://github.com/vweevers/module-error/releases/tag/v1.0.0 diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/LICENSE new file mode 100644 index 0000000..3a9356b --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Vincent Weevers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/README.md new file mode 100644 index 0000000..ec97ff8 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/README.md @@ -0,0 +1,89 @@ +# module-error + +**Create errors with `code` and `cause` properties. Simple and extensible.** + +[![npm status](http://img.shields.io/npm/v/module-error.svg)](https://www.npmjs.org/package/module-error) +[![node](https://img.shields.io/node/v/module-error.svg)](https://www.npmjs.org/package/module-error) +[![Test](https://img.shields.io/github/workflow/status/vweevers/module-error/Test?label=test)](https://github.com/vweevers/module-error/actions/workflows/test.yml) +[![Standard](https://img.shields.io/badge/standard-informational?logo=javascript&logoColor=fff)](https://standardjs.com) +[![Common Changelog](https://common-changelog.org/badge.svg)](https://common-changelog.org) + +## Usage + +Works like a regular `Error` constructor but adds an options argument (as [`proposal-error-cause`](https://github.com/tc39/proposal-error-cause) does). + +```js +const ModuleError = require('module-error') + +throw new ModuleError('Message goes here', { + code: 'EXAMPLE_NOT_FOUND' +}) +``` + +The primary purpose of `ModuleError` is to define a `code` property on the error object following Node.js conventions. It should be set to an uppercase string that uniquely identifies a situation, prefixed with the name of your module (or a collection of modules) to prevent conflicts. + +The output looks like this in Node.js (some stack frames omitted for brevity): + +``` +ModuleError: Message goes here + at Object. (/home/app/example.js:5:7) + at node:internal/main/run_main_module:17:47 { + code: 'EXAMPLE_NOT_FOUND' +} +``` + +The benefit of error codes is that messages can be changed without a semver-major release because your [semver](https://semver.org) contract will be on the codes. Codes can be reused across related modules while allowing individual modules to customize messages. I also prefer it over `instanceof MyError` logic because codes work cross-realm and when a tree of `node_modules` contains multiple versions of a module. + +To wrap another error: + +```js +try { + JSON.parse(await fs.readFile('state.json')) +} catch (err) { + throw new ModuleError('Could not load state', { + code: 'EXAMPLE_INVALID_STATE', + cause: err + }) +} +``` + +If for convenience you want to create subclasses with prepared codes: + +```js +class NotFoundError extends ModuleError { + constructor(message, options) { + super(message, { ...options, code: 'EXAMPLE_NOT_FOUND' }) + } +} +``` + +Then you can do: + +```js +throw new NotFoundError('Message goes here') +``` + +Under Node.js the stack trace will be adjusted accordingly, to skip the frame containing your error constructor. + +## API + +### `ModuleError(message, [options])` + +Constructor to create an error with the provided `message` string. Options: + +- `code` (string): if provided, define a `code` property with this value. +- `cause` (Error): if provided, define a `cause` property with this value. Unlike the spec of [`proposal-error-cause`](https://github.com/tc39/proposal-error-cause) the property is enumerable so that Node.js (v16 at the time of writing) will print it. Firefox prints it regardless, Chromium doesn't yet. +- `expected`: if truthy, define a `expected` property with value `true`. This is useful for command line interfaces to differentiate unexpected errors from e.g. invalid user input. A pattern I like to follow is to print only an error message if `err.expected` is true and no `--verbose` flag was provided. Otherwise print the full stack. +- `transient`: if truthy, define a `transient` property with value `true`. This communicates to users that the operation that caused the error may be retried. See also [`transient-error`](https://github.com/vweevers/transient-error) to mark existing errors as transient. + +## Install + +With [npm](https://npmjs.org) do: + +``` +npm install module-error +``` + +## License + +[MIT](LICENSE) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/index.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/index.d.ts new file mode 100644 index 0000000..18600cf --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/index.d.ts @@ -0,0 +1,18 @@ +declare class ModuleError extends Error { + /** + * @param message Error message + */ + constructor(message: string, options?: { + code?: string + cause?: Error + expected?: boolean + transient?: boolean + } | undefined) + + code: string | undefined + expected: boolean | undefined + transient: boolean | undefined + cause: Error | undefined +} + +export = ModuleError diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/index.js new file mode 100644 index 0000000..b0daad4 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/index.js @@ -0,0 +1,22 @@ +'use strict' + +module.exports = class ModuleError extends Error { + /** + * @param {string} message Error message + * @param {{ code?: string, cause?: Error, expected?: boolean, transient?: boolean }} [options] + */ + constructor (message, options) { + super(message || '') + + if (typeof options === 'object' && options !== null) { + if (options.code) this.code = String(options.code) + if (options.expected) this.expected = true + if (options.transient) this.transient = true + if (options.cause) this.cause = options.cause + } + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor) + } + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/package.json new file mode 100644 index 0000000..87c7b9d --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/module-error/package.json @@ -0,0 +1,36 @@ +{ + "name": "module-error", + "version": "1.0.2", + "description": "Create errors with code and cause properties", + "license": "MIT", + "author": "Vincent Weevers", + "scripts": { + "test": "standard && tsc && hallmark && node test", + "build": "tsc -d --emitDeclarationOnly --noEmit false" + }, + "types": "./index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "CHANGELOG.md" + ], + "devDependencies": { + "@types/node": "^17.0.6", + "@voxpelli/tsconfig": "^3.0.0", + "hallmark": "^4.0.0", + "standard": "^16.0.4", + "tape": "^5.3.1", + "typescript": "^4.4.4" + }, + "keywords": [ + "cause", + "code", + "error" + ], + "repository": "vweevers/module-error", + "bugs": "https://github.com/vweevers/module-error/issues", + "homepage": "https://github.com/vweevers/module-error", + "engines": { + "node": ">=10" + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/LICENSE new file mode 100644 index 0000000..cb757e5 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Mathias Buus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/README.md new file mode 100644 index 0000000..1691845 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/README.md @@ -0,0 +1,277 @@ +# napi-macros + +Set of utility macros to make writing [N-API](https://nodejs.org/dist/latest-v9.x/docs/api/n-api.html) modules a little easier. + +``` +npm install napi-macros +``` + +Then add the following to your target in your binding.gyp file + +``` +"include_dirs": [ + " +#include + +NAPI_METHOD(times_two) { + NAPI_ARGV(1) + NAPI_ARGV_INT32(number, 0) + + number *= 2; + + NAPI_RETURN_INT32(number) +} + +NAPI_INIT() { + NAPI_EXPORT_FUNCTION(times_two) +} +``` + +Full working example can be found in the [example/](https://github.com/mafintosh/napi-macros/tree/master/example) folder. + +## API + +#### `NAPI_INIT()` + +Setup init boilerplate. Pass the function body after. + +``` c +static char *my_string = "hello"; + +NAPI_INIT() { + EXPORT_STRING(my_string) +} +``` + +#### `NAPI_METHOD(name)` + +Setup method boilerplate. Pass the function body after. + +``` c +NAPI_METHOD(add) { + NAPI_ARGV(2) + NAPI_ARGV_INT32(a, 0) + NAPI_ARGV_INT32(b, 1) + + a = a + b + + NAPI_RETURN_INT32(a) +} +``` + +#### `NAPI_ARGV(n)` + +Setup up argv boilerplate. `n` is how many arguments you are expecting. +Expects the `napi_env` to be in scope as `env` and the `napi_callback_info` to be in scope as `info`. + +#### `NAPI_ARGV_BUFFER(name, index)` + +Get a buffer out of the arguments at the corresponding index. +Sets `char *name` and `size_t name_len` with the buffer and buffer length. + +#### `NAPI_ARGV_BUFFER_CAST(type, name, index)` + +Get a buffer out and cast the pointer to the specified type. +Note that the type should include the pointer star, i.e. + +``` c +NAPI_ARGV_BUFFER_CAST(uv_udp_t *, handle, 0) +``` + +Will cast the 1st argument as `uv_udp_t` pointer. + +#### `NAPI_ARGV_UINT32(name, index)` + +Get an argument as a uint32. +Will throw if argument is not the right type. + +#### `NAPI_ARGV_INT32(name, index)` + +Get an argument as an int32. +Will throw if argument is not the right type. + +#### `NAPI_ARGV_UTF8(name, length, index)` + +Get an argument as a utf8 string. + +`name` will be a `char[length]` array. + +Will throw if argument is not the right type. + +#### `NAPI_ARGV_UTF8_MALLOC(name, index)` + +Get an argument as a utf8 string. + +`name` will be a `char*`. + +Like `NAPI_ARGV_UTF8()` but allocates `name` on the heap using `malloc()`, which should be `free()`'d after usage. + +#### `NAPI_BUFFER_CAST(type, name, var)` + +Same as `NAPI_ARGV_BUFFER_CAST` but takes a generic `napi_value` variable instead of an argv index. + +#### `NAPI_BUFFER(name, var)` + +Same as `NAPI_ARGV_BUFFER` but takes a generic `napi_value` variable instead of an argv index. + +#### `NAPI_UTF8(name, size, var)` + +Same as `NAPI_ARGV_UTF8` but takes a generic `napi_value` variable instead of an argv index. + +#### `NAPI_UTF8_MALLOC(name, var)` + +Same as `NAPI_ARGV_UTF8_MALLOC` but takes a generic `napi_value` variable instead of an argv index. + +#### `NAPI_UINT32(name, var)` + +Same as `NAPI_ARGV_UINT32` but takes a generic `napi_value` variable instead of an argv index. + +#### `NAPI_INT32(name, var)` + +Same as `NAPI_ARGV_INT32` but takes a generic `napi_value` variable instead of an argv index. + +#### `NAPI_EXPORT_FUNCTION(fn)` + +Will export a function in the Init method. Expects the env and `exports` to be in scope. +The name of the exported function is the same name as the c function. + +#### `NAPI_EXPORT_SIZEOF(struct)` + +Export the size of a strict. The exported name is `sizeof_{struct-name}`. + +#### `NAPI_EXPORT_UINT32(name)` + +Export a uint32. +The name of the exported number is the same name as the c variable. + +#### `NAPI_EXPORT_INT32(name)` + +Export an int32. +The name of the exported number is the same name as the c variable. + +#### `NAPI_EXPORT_UTF8(name, len)` + +Export a utf8 string. `len` should be the length of the string. +The name of the exported string is the same name as the c variable. + +#### `NAPI_EXPORT_STRING(name)` + +Export a null terminated string. +The name of the exported string is the same name as the c variable. + +#### `NAPI_EXPORT_SIZEOF(type)` + +Exports `sizeof(type)`. +The name of the exported number is the same name as the c variable. + +#### `NAPI_EXPORT_SIZEOF_STRUCT(structName)` + +Exports `sizeof(struct structName)`. +The name of the exported number is the same name as the c variable. + +#### `NAPI_EXPORT_ALIGNMENTOF(type)` + +Exports the byte alignment of `type`. +The name of the exported number is the same name as the c variable. + +#### `NAPI_EXPORT_ALIGNMENTOF_STRUCT(structName)` + +Exports the byte alignment of `struct structName`. +The name of the exported number is the same name as the c variable. + +#### `NAPI_EXPORT_OFFSETOF(type, name)` + +Exports the byte offset of `name` within `type`. +The name of the exported number is the same name as the c variables. + +#### `NAPI_EXPORT_OFFSETOF_STRUCT(structName, name)` + +Exports the byte offset of `name` within `struct structName`. +The name of the exported number is the same name as the c variables. + +#### `NAPI_FOR_EACH(array, element)` + +Iterate over an array. `array` should be a `napi_value` containing a javascript array +and `element` is the variable name an element will be exposed as. Expects the loop body +to be passed after. + +``` c +napi_value buffers = argv[0] // first argument is a js array +NAPI_FOR_EACH(buffers, buffer) { + NAPI_BUFFER(cbuf, buffer) + printf("cbuf is now a char * pointer: %s\n", cbuf); +} +``` + +#### `NAPI_RETURN_UINT32(name)` + +Returns a uint32. + +#### `NAPI_RETURN_INT32(name)` + +Returns an int32. + +#### `NAPI_RETURN_UTF8(name, len)` + +Return a utf8 string. `len` should be the length of the string. + +#### `NAPI_RETURN_STRING(name)` + +Return a null terminated string. + +#### `NAPI_STATUS_THROWS(call)` + +Checks the return status of any `napi_*` function returning a `napi_status` type. This simplifies using a `napi_status` variable and comparing the result with `napi_ok`. It's used internally but can be used stand alone as well. + +```c +NAPI_STATUS_THROWS( + napi_create_threadsafe_function( + NULL, + callback, + 0, + async_resource_name, + 0, + 3, + 0, + my_finalize, + NULL, + my_callback, + &threadsafe_function + ) +); +``` + +Above example will fail because the first `env` parameter is `NULL` and throw the following error: + +``` +Error: napi_create_threadsafe_function(NULL, callback, 0, async_resource_name, 0, 3, 0, my_finalize, \ +NULL, my_callback, &threadsafe_function) failed! +``` + +#### `NAPI_UV_THROWS(err, fn)` + +Checks if a libuv call fails and if so, throws an error. + +``` c +int err; +NAPI_UV_THROWS(err, uv_ip4_addr((char *) &ip, port, &addr)) +``` + +#### `NAPI_MAKE_CALLBACK(env, async_ctx, ctx, func, argc, argv, result)` + +Same as `napi_make_callback` except it checks if the JS function throw an exception +and triggers a `process.on('uncaughtException')` if so. + +## License + +MIT diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/index.js new file mode 100644 index 0000000..29c169e --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/index.js @@ -0,0 +1 @@ +console.log(require('path').relative('.', __dirname)) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/napi-macros.h b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/napi-macros.h new file mode 100644 index 0000000..127d559 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/napi-macros.h @@ -0,0 +1,255 @@ +#ifndef NAPI_MACROS +#define NAPI_MACROS + +#define NAPI_INIT() \ + static void napi_macros_init(napi_env env, napi_value exports); \ + static napi_value napi_macros_init_wrap (napi_env env, napi_value exports) { \ + napi_macros_init(env, exports); \ + return exports; \ + } \ + NAPI_MODULE(NODE_GYP_MODULE_NAME, napi_macros_init_wrap) \ + static void napi_macros_init (napi_env env, napi_value exports) + +#define NAPI_TEST_GC(env) \ + { \ + napi_handle_scope scope; \ + napi_open_handle_scope(env, &scope); \ + napi_value s; \ + napi_value r; \ + napi_create_string_utf8(env, "try { global.gc() } catch {}", NAPI_AUTO_LENGTH, &s); \ + napi_run_script(env, s, &r); \ + napi_close_handle_scope(env, scope); \ + } + +#define NAPI_UV_ERROR_MAP_ITER(NAME, DESC) { \ + napi_create_array(env, &entry); \ + napi_create_array(env, &val); \ + napi_value name; \ + napi_create_string_utf8(env, #NAME, NAPI_AUTO_LENGTH, &name); \ + napi_set_element(env, val, 0, name); \ + napi_value desc; \ + napi_create_string_utf8(env, DESC, NAPI_AUTO_LENGTH, &desc); \ + napi_set_element(env, val, 1, desc); \ + napi_create_int32(env, UV_ ## NAME, &key); \ + napi_set_element(env, entry, 0, key); \ + napi_set_element(env, entry, 1, val); \ + napi_set_element(env, arr, i++, entry); \ +} + +#define NAPI_MAKE_CALLBACK(env, nil, ctx, cb, n, argv, res) \ + if (napi_make_callback(env, nil, ctx, cb, n, argv, res) == napi_pending_exception) { \ + napi_value fatal_exception; \ + napi_get_and_clear_last_exception(env, &fatal_exception); \ + napi_fatal_exception(env, fatal_exception); \ + } + +#define NAPI_STATUS_THROWS_VOID(call) \ + if ((call) != napi_ok) { \ + napi_throw_error(env, NULL, #call " failed!"); \ + return; \ + } + +#define NAPI_STATUS_THROWS(call) \ + if ((call) != napi_ok) { \ + napi_throw_error(env, NULL, #call " failed!"); \ + return NULL; \ + } + +#define NAPI_METHOD(name) \ + napi_value name (napi_env env, napi_callback_info info) + +#define NAPI_UV_THROWS(err, fn) \ + err = fn; \ + if (err < 0) { \ + napi_throw_error(env, uv_err_name(err), uv_strerror(err)); \ + return NULL; \ + } + +#define NAPI_EXPORT_OFFSETOF(type, name) \ + { \ + napi_value name##_offsetof; \ + type tmp; \ + void *ptr = &(tmp.name); \ + void *ptr_base = &tmp; \ + int offset = (char *) ptr - (char *) ptr_base; \ + NAPI_STATUS_THROWS_VOID(napi_create_uint32(env, offset, &name##_offsetof)) \ + NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, "offsetof_" #type "_" #name, name##_offsetof)) \ + } + +#define NAPI_EXPORT_OFFSETOF_STRUCT(type, name) \ + { \ + napi_value name##_offsetof; \ + struct type tmp; \ + void *ptr = &(tmp.name); \ + void *ptr_base = &tmp; \ + int offset = (char *) ptr - (char *) ptr_base; \ + NAPI_STATUS_THROWS_VOID(napi_create_uint32(env, offset, &name##_offsetof)) \ + NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, "offsetof_struct_" #type "_" #name, name##_offsetof)) \ + } + + +#define NAPI_EXPORT_ALIGNMENTOF(name) \ + { \ + napi_value name##_alignmentof; \ + struct tmp { \ + char a; \ + name b; \ + }; \ + NAPI_STATUS_THROWS_VOID(napi_create_uint32(env, sizeof(struct tmp) - sizeof(name), &name##_alignmentof)) \ + NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, "alignmentof_" #name, name##_alignmentof)) \ + } + +#define NAPI_EXPORT_ALIGNMENTOF_STRUCT(name) \ + { \ + napi_value name##_alignmentof; \ + struct tmp { \ + char a; \ + struct name b; \ + }; \ + NAPI_STATUS_THROWS_VOID(napi_create_uint32(env, sizeof(struct tmp) - sizeof(struct name), &name##_alignmentof)) \ + NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, "alignmentof_" #name, name##_alignmentof)) \ + } + +#define NAPI_EXPORT_UV_ERROR_MAP() \ + { \ + napi_value arr; \ + napi_value key; \ + napi_value val; \ + napi_value entry; \ + napi_create_array(env, &arr); \ + int i = 0; \ + UV_ERRNO_MAP(NAPI_UV_ERROR_MAP_ITER) \ + NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, "uv_error_map", arr)) \ + } + +#define NAPI_EXPORT_SIZEOF(name) \ + { \ + napi_value name##_sizeof; \ + NAPI_STATUS_THROWS_VOID(napi_create_uint32(env, sizeof(name), &name##_sizeof)) \ + NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, "sizeof_" #name, name##_sizeof)) \ + } + +#define NAPI_EXPORT_SIZEOF_STRUCT(name) \ + { \ + napi_value name##_sizeof; \ + NAPI_STATUS_THROWS_VOID(napi_create_uint32(env, sizeof(struct name), &name##_sizeof)) \ + NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, "sizeof_" #name, name##_sizeof)) \ + } + +#define NAPI_EXPORT_UINT32(name) \ + { \ + napi_value name##_uint32; \ + NAPI_STATUS_THROWS_VOID(napi_create_uint32(env, name, &name##_uint32)) \ + NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, #name, name##_uint32)) \ + } + +#define NAPI_EXPORT_INT32(name) \ + { \ + napi_value name##_int32; \ + NAPI_STATUS_THROWS_VOID(napi_create_int32(env, name, &name##_int32)) \ + NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, #name, name##_int32)) \ + } + +#define NAPI_EXPORT_FUNCTION(name) \ + { \ + napi_value name##_fn; \ + NAPI_STATUS_THROWS_VOID(napi_create_function(env, NULL, 0, name, NULL, &name##_fn)) \ + NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, #name, name##_fn)) \ + } + +#define NAPI_EXPORT_UTF8(name, len) \ + { \ + napi_value name##_utf8; \ + NAPI_STATUS_THROWS_VOID(napi_create_string_utf8(env, name, len, &name##_utf8)) \ + NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, #name, name##_utf8)) \ + } + +#define NAPI_EXPORT_STRING(name) \ + NAPI_EXPORT_UTF8(name, NAPI_AUTO_LENGTH) + +#define NAPI_RETURN_INT32(name) \ + napi_value return_int32; \ + NAPI_STATUS_THROWS(napi_create_int32(env, name, &return_int32)) \ + return return_int32; + +#define NAPI_RETURN_UINT32(name) \ + napi_value return_uint32; \ + NAPI_STATUS_THROWS(napi_create_uint32(env, name, &return_uint32)) \ + return return_uint32; + +#define NAPI_RETURN_UTF8(name, len) \ + napi_value return_utf8; \ + NAPI_STATUS_THROWS(napi_create_string_utf8(env, name, len, &return_utf8)) \ + return return_utf8; + +#define NAPI_RETURN_STRING(name) \ + NAPI_RETURN_UTF8(name, NAPI_AUTO_LENGTH) + +#define NAPI_UTF8(name, size, val) \ + char name[size]; \ + size_t name##_len; \ + if (napi_get_value_string_utf8(env, val, (char *) &name, size, &name##_len) != napi_ok) { \ + napi_throw_error(env, "EINVAL", "Expected string"); \ + return NULL; \ + } + +#define NAPI_UTF8_MALLOC(name, val) \ + size_t name##_size = 0; \ + NAPI_STATUS_THROWS(napi_get_value_string_utf8(env, val, NULL, 0, &name##_size)) \ + char* name = (char*)malloc((name##_size + 1) * sizeof(char)); \ + size_t name##_len; \ + NAPI_STATUS_THROWS(napi_get_value_string_utf8(env, val, name, name##_size + 1, &name##_len)) \ + name[name##_size] = '\0'; + +#define NAPI_UINT32(name, val) \ + uint32_t name; \ + if (napi_get_value_uint32(env, val, &name) != napi_ok) { \ + napi_throw_error(env, "EINVAL", "Expected unsigned number"); \ + return NULL; \ + } + +#define NAPI_INT32(name, val) \ + int32_t name; \ + if (napi_get_value_int32(env, val, &name) != napi_ok) { \ + napi_throw_error(env, "EINVAL", "Expected number"); \ + return NULL; \ + } + +#define NAPI_BUFFER_CAST(type, name, val) \ + type name; \ + size_t name##_len; \ + NAPI_STATUS_THROWS(napi_get_buffer_info(env, val, (void **) &name, &name##_len)) + +#define NAPI_BUFFER(name, val) \ + NAPI_BUFFER_CAST(char *, name, val) + +#define NAPI_FOR_EACH(arr, element) \ + uint32_t arr##_len; \ + napi_get_array_length(env, arr, &arr##_len); \ + napi_value element; \ + for (uint32_t i = 0; i < arr##_len && napi_get_element(env, arr, i, &element) == napi_ok; i++) + +#define NAPI_ARGV(n) \ + napi_value argv[n]; \ + size_t argc = n; \ + NAPI_STATUS_THROWS(napi_get_cb_info(env, info, &argc, argv, NULL, NULL)) + +#define NAPI_ARGV_UTF8(name, size, i) \ + NAPI_UTF8(name, size, argv[i]) + +#define NAPI_ARGV_UTF8_MALLOC(name, i) \ + NAPI_UTF8_MALLOC(name, argv[i]) + +#define NAPI_ARGV_UINT32(name, i) \ + NAPI_UINT32(name, argv[i]) + +#define NAPI_ARGV_INT32(name, i) \ + NAPI_INT32(name, argv[i]) + +#define NAPI_ARGV_BUFFER_CAST(type, name, i) \ + NAPI_BUFFER_CAST(type, name, argv[i]) + +#define NAPI_ARGV_BUFFER(name, i) \ + NAPI_ARGV_BUFFER_CAST(char *, name, i) + +#endif diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/package.json new file mode 100644 index 0000000..496a3d6 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/napi-macros/package.json @@ -0,0 +1,22 @@ +{ + "name": "napi-macros", + "version": "2.2.2", + "description": "Set of utility macros to make writing N-API modules a little easier.", + "main": "index.js", + "dependencies": {}, + "devDependencies": {}, + "repository": { + "type": "git", + "url": "https://github.com/mafintosh/napi-macros.git" + }, + "files": [ + "napi-macros.h", + "index.js" + ], + "author": "Mathias Buus (@mafintosh)", + "license": "MIT", + "bugs": { + "url": "https://github.com/mafintosh/napi-macros/issues" + }, + "homepage": "https://github.com/mafintosh/napi-macros" +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/LICENSE new file mode 100644 index 0000000..56fce08 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Mathias Buus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/README.md new file mode 100644 index 0000000..f712ca6 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/README.md @@ -0,0 +1,58 @@ +# node-gyp-build + +> Build tool and bindings loader for [`node-gyp`][node-gyp] that supports prebuilds. + +``` +npm install node-gyp-build +``` + +[![Test](https://github.com/prebuild/node-gyp-build/actions/workflows/test.yml/badge.svg)](https://github.com/prebuild/node-gyp-build/actions/workflows/test.yml) + +Use together with [`prebuildify`][prebuildify] to easily support prebuilds for your native modules. + +## Usage + +> **Note.** Prebuild names have changed in [`prebuildify@3`][prebuildify] and `node-gyp-build@4`. Please see the documentation below. + +`node-gyp-build` works similar to [`node-gyp build`][node-gyp] except that it will check if a build or prebuild is present before rebuilding your project. + +It's main intended use is as an npm install script and bindings loader for native modules that bundle prebuilds using [`prebuildify`][prebuildify]. + +First add `node-gyp-build` as an install script to your native project + +``` js +{ + ... + "scripts": { + "install": "node-gyp-build" + } +} +``` + +Then in your `index.js`, instead of using the [`bindings`](https://www.npmjs.com/package/bindings) module use `node-gyp-build` to load your binding. + +``` js +var binding = require('node-gyp-build')(__dirname) +``` + +If you do these two things and bundle prebuilds with [`prebuildify`][prebuildify] your native module will work for most platforms +without having to compile on install time AND will work in both node and electron without the need to recompile between usage. + +Users can override `node-gyp-build` and force compiling by doing `npm install --build-from-source`. + +Prebuilds will be attempted loaded from `MODULE_PATH/prebuilds/...` and then next `EXEC_PATH/prebuilds/...` (the latter allowing use with `zeit/pkg`) + +## Supported prebuild names + +If so desired you can bundle more specific flavors, for example `musl` builds to support Alpine, or targeting a numbered ARM architecture version. + +These prebuilds can be bundled in addition to generic prebuilds; `node-gyp-build` will try to find the most specific flavor first. Prebuild filenames are composed of _tags_. The runtime tag takes precedence, as does an `abi` tag over `napi`. For more details on tags, please see [`prebuildify`][prebuildify]. + +Values for the `libc` and `armv` tags are auto-detected but can be overridden through the `LIBC` and `ARM_VERSION` environment variables, respectively. + +## License + +MIT + +[prebuildify]: https://github.com/prebuild/prebuildify +[node-gyp]: https://www.npmjs.com/package/node-gyp diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/bin.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/bin.js new file mode 100644 index 0000000..3fbcdf0 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/bin.js @@ -0,0 +1,78 @@ +#!/usr/bin/env node + +var proc = require('child_process') +var os = require('os') +var path = require('path') + +if (!buildFromSource()) { + proc.exec('node-gyp-build-test', function (err, stdout, stderr) { + if (err) { + if (verbose()) console.error(stderr) + preinstall() + } + }) +} else { + preinstall() +} + +function build () { + var args = [os.platform() === 'win32' ? 'node-gyp.cmd' : 'node-gyp', 'rebuild'] + + try { + var pkg = require('node-gyp/package.json') + args = [ + process.execPath, + path.join(require.resolve('node-gyp/package.json'), '..', typeof pkg.bin === 'string' ? pkg.bin : pkg.bin['node-gyp']), + 'rebuild' + ] + } catch (_) {} + + proc.spawn(args[0], args.slice(1), { stdio: 'inherit' }).on('exit', function (code) { + if (code || !process.argv[3]) process.exit(code) + exec(process.argv[3]).on('exit', function (code) { + process.exit(code) + }) + }) +} + +function preinstall () { + if (!process.argv[2]) return build() + exec(process.argv[2]).on('exit', function (code) { + if (code) process.exit(code) + build() + }) +} + +function exec (cmd) { + if (process.platform !== 'win32') { + var shell = os.platform() === 'android' ? 'sh' : '/bin/sh' + return proc.spawn(shell, ['-c', '--', cmd], { + stdio: 'inherit' + }) + } + + return proc.spawn(process.env.comspec || 'cmd.exe', ['/s', '/c', '"' + cmd + '"'], { + windowsVerbatimArguments: true, + stdio: 'inherit' + }) +} + +function buildFromSource () { + return hasFlag('--build-from-source') || process.env.npm_config_build_from_source === 'true' +} + +function verbose () { + return hasFlag('--verbose') || process.env.npm_config_loglevel === 'verbose' +} + +// TODO (next major): remove in favor of env.npm_config_* which works since npm +// 0.1.8 while npm_config_argv will stop working in npm 7. See npm/rfcs#90 +function hasFlag (flag) { + if (!process.env.npm_config_argv) return false + + try { + return JSON.parse(process.env.npm_config_argv).original.indexOf(flag) !== -1 + } catch (_) { + return false + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/build-test.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/build-test.js new file mode 100644 index 0000000..b6622a5 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/build-test.js @@ -0,0 +1,19 @@ +#!/usr/bin/env node + +process.env.NODE_ENV = 'test' + +var path = require('path') +var test = null + +try { + var pkg = require(path.join(process.cwd(), 'package.json')) + if (pkg.name && process.env[pkg.name.toUpperCase().replace(/-/g, '_')]) { + process.exit(0) + } + test = pkg.prebuild.test +} catch (err) { + // do nothing +} + +if (test) require(path.join(process.cwd(), test)) +else require('./')() diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/index.js new file mode 100644 index 0000000..07eb14f --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/index.js @@ -0,0 +1,6 @@ +const runtimeRequire = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require // eslint-disable-line +if (typeof runtimeRequire.addon === 'function') { // if the platform supports native resolving prefer that + module.exports = runtimeRequire.addon.bind(runtimeRequire) +} else { // else use the runtime version here + module.exports = require('./node-gyp-build.js') +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/node-gyp-build.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/node-gyp-build.js new file mode 100644 index 0000000..76b96e1 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/node-gyp-build.js @@ -0,0 +1,207 @@ +var fs = require('fs') +var path = require('path') +var os = require('os') + +// Workaround to fix webpack's build warnings: 'the request of a dependency is an expression' +var runtimeRequire = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require // eslint-disable-line + +var vars = (process.config && process.config.variables) || {} +var prebuildsOnly = !!process.env.PREBUILDS_ONLY +var abi = process.versions.modules // TODO: support old node where this is undef +var runtime = isElectron() ? 'electron' : (isNwjs() ? 'node-webkit' : 'node') + +var arch = process.env.npm_config_arch || os.arch() +var platform = process.env.npm_config_platform || os.platform() +var libc = process.env.LIBC || (isAlpine(platform) ? 'musl' : 'glibc') +var armv = process.env.ARM_VERSION || (arch === 'arm64' ? '8' : vars.arm_version) || '' +var uv = (process.versions.uv || '').split('.')[0] + +module.exports = load + +function load (dir) { + return runtimeRequire(load.resolve(dir)) +} + +load.resolve = load.path = function (dir) { + dir = path.resolve(dir || '.') + + try { + var name = runtimeRequire(path.join(dir, 'package.json')).name.toUpperCase().replace(/-/g, '_') + if (process.env[name + '_PREBUILD']) dir = process.env[name + '_PREBUILD'] + } catch (err) {} + + if (!prebuildsOnly) { + var release = getFirst(path.join(dir, 'build/Release'), matchBuild) + if (release) return release + + var debug = getFirst(path.join(dir, 'build/Debug'), matchBuild) + if (debug) return debug + } + + var prebuild = resolve(dir) + if (prebuild) return prebuild + + var nearby = resolve(path.dirname(process.execPath)) + if (nearby) return nearby + + var target = [ + 'platform=' + platform, + 'arch=' + arch, + 'runtime=' + runtime, + 'abi=' + abi, + 'uv=' + uv, + armv ? 'armv=' + armv : '', + 'libc=' + libc, + 'node=' + process.versions.node, + process.versions.electron ? 'electron=' + process.versions.electron : '', + typeof __webpack_require__ === 'function' ? 'webpack=true' : '' // eslint-disable-line + ].filter(Boolean).join(' ') + + throw new Error('No native build was found for ' + target + '\n loaded from: ' + dir + '\n') + + function resolve (dir) { + // Find matching "prebuilds/-" directory + var tuples = readdirSync(path.join(dir, 'prebuilds')).map(parseTuple) + var tuple = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0] + if (!tuple) return + + // Find most specific flavor first + var prebuilds = path.join(dir, 'prebuilds', tuple.name) + var parsed = readdirSync(prebuilds).map(parseTags) + var candidates = parsed.filter(matchTags(runtime, abi)) + var winner = candidates.sort(compareTags(runtime))[0] + if (winner) return path.join(prebuilds, winner.file) + } +} + +function readdirSync (dir) { + try { + return fs.readdirSync(dir) + } catch (err) { + return [] + } +} + +function getFirst (dir, filter) { + var files = readdirSync(dir).filter(filter) + return files[0] && path.join(dir, files[0]) +} + +function matchBuild (name) { + return /\.node$/.test(name) +} + +function parseTuple (name) { + // Example: darwin-x64+arm64 + var arr = name.split('-') + if (arr.length !== 2) return + + var platform = arr[0] + var architectures = arr[1].split('+') + + if (!platform) return + if (!architectures.length) return + if (!architectures.every(Boolean)) return + + return { name, platform, architectures } +} + +function matchTuple (platform, arch) { + return function (tuple) { + if (tuple == null) return false + if (tuple.platform !== platform) return false + return tuple.architectures.includes(arch) + } +} + +function compareTuples (a, b) { + // Prefer single-arch prebuilds over multi-arch + return a.architectures.length - b.architectures.length +} + +function parseTags (file) { + var arr = file.split('.') + var extension = arr.pop() + var tags = { file: file, specificity: 0 } + + if (extension !== 'node') return + + for (var i = 0; i < arr.length; i++) { + var tag = arr[i] + + if (tag === 'node' || tag === 'electron' || tag === 'node-webkit') { + tags.runtime = tag + } else if (tag === 'napi') { + tags.napi = true + } else if (tag.slice(0, 3) === 'abi') { + tags.abi = tag.slice(3) + } else if (tag.slice(0, 2) === 'uv') { + tags.uv = tag.slice(2) + } else if (tag.slice(0, 4) === 'armv') { + tags.armv = tag.slice(4) + } else if (tag === 'glibc' || tag === 'musl') { + tags.libc = tag + } else { + continue + } + + tags.specificity++ + } + + return tags +} + +function matchTags (runtime, abi) { + return function (tags) { + if (tags == null) return false + if (tags.runtime && tags.runtime !== runtime && !runtimeAgnostic(tags)) return false + if (tags.abi && tags.abi !== abi && !tags.napi) return false + if (tags.uv && tags.uv !== uv) return false + if (tags.armv && tags.armv !== armv) return false + if (tags.libc && tags.libc !== libc) return false + + return true + } +} + +function runtimeAgnostic (tags) { + return tags.runtime === 'node' && tags.napi +} + +function compareTags (runtime) { + // Precedence: non-agnostic runtime, abi over napi, then by specificity. + return function (a, b) { + if (a.runtime !== b.runtime) { + return a.runtime === runtime ? -1 : 1 + } else if (a.abi !== b.abi) { + return a.abi ? -1 : 1 + } else if (a.specificity !== b.specificity) { + return a.specificity > b.specificity ? -1 : 1 + } else { + return 0 + } + } +} + +function isNwjs () { + return !!(process.versions && process.versions.nw) +} + +function isElectron () { + if (process.versions && process.versions.electron) return true + if (process.env.ELECTRON_RUN_AS_NODE) return true + return typeof window !== 'undefined' && window.process && window.process.type === 'renderer' +} + +function isAlpine (platform) { + return platform === 'linux' && fs.existsSync('/etc/alpine-release') +} + +// Exposed for unit tests +// TODO: move to lib +load.parseTags = parseTags +load.matchTags = matchTags +load.compareTags = compareTags +load.parseTuple = parseTuple +load.matchTuple = matchTuple +load.compareTuples = compareTuples diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/optional.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/optional.js new file mode 100644 index 0000000..8daa04a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/optional.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node + +/* +I am only useful as an install script to make node-gyp not compile for purely optional native deps +*/ + +process.exit(0) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/package.json new file mode 100644 index 0000000..86319f3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/node-gyp-build/package.json @@ -0,0 +1,29 @@ +{ + "name": "node-gyp-build", + "version": "4.8.0", + "description": "Build tool and bindings loader for node-gyp that supports prebuilds", + "main": "index.js", + "devDependencies": { + "array-shuffle": "^1.0.1", + "standard": "^14.0.0", + "tape": "^5.0.0" + }, + "scripts": { + "test": "standard && node test" + }, + "bin": { + "node-gyp-build": "./bin.js", + "node-gyp-build-optional": "./optional.js", + "node-gyp-build-test": "./build-test.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/prebuild/node-gyp-build.git" + }, + "author": "Mathias Buus (@mafintosh)", + "license": "MIT", + "bugs": { + "url": "https://github.com/prebuild/node-gyp-build/issues" + }, + "homepage": "https://github.com/prebuild/node-gyp-build" +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/LICENSE new file mode 100644 index 0000000..c7e6852 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) Feross Aboukhadijeh + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/README.md new file mode 100644 index 0000000..0be05a6 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/README.md @@ -0,0 +1,90 @@ +# queue-microtask [![ci][ci-image]][ci-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url] + +[ci-image]: https://img.shields.io/github/workflow/status/feross/queue-microtask/ci/master +[ci-url]: https://github.com/feross/queue-microtask/actions +[npm-image]: https://img.shields.io/npm/v/queue-microtask.svg +[npm-url]: https://npmjs.org/package/queue-microtask +[downloads-image]: https://img.shields.io/npm/dm/queue-microtask.svg +[downloads-url]: https://npmjs.org/package/queue-microtask +[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg +[standard-url]: https://standardjs.com + +### fast, tiny [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/queueMicrotask) shim for modern engines + +- Use [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/queueMicrotask) in all modern JS engines. +- No dependencies. Less than 10 lines. No shims or complicated fallbacks. +- Optimal performance in all modern environments + - Uses `queueMicrotask` in modern environments + - Fallback to `Promise.resolve().then(fn)` in Node.js 10 and earlier, and old browsers (same performance as `queueMicrotask`) + +## install + +``` +npm install queue-microtask +``` + +## usage + +```js +const queueMicrotask = require('queue-microtask') + +queueMicrotask(() => { /* this will run soon */ }) +``` + +## What is `queueMicrotask` and why would one use it? + +The `queueMicrotask` function is a WHATWG standard. It queues a microtask to be executed prior to control returning to the event loop. + +A microtask is a short function which will run after the current task has completed its work and when there is no other code waiting to be run before control of the execution context is returned to the event loop. + +The code `queueMicrotask(fn)` is equivalent to the code `Promise.resolve().then(fn)`. It is also very similar to [`process.nextTick(fn)`](https://nodejs.org/api/process.html#process_process_nexttick_callback_args) in Node. + +Using microtasks lets code run without interfering with any other, potentially higher priority, code that is pending, but before the JS engine regains control over the execution context. + +See the [spec](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#microtask-queuing) or [Node documentation](https://nodejs.org/api/globals.html#globals_queuemicrotask_callback) for more information. + +## Who is this package for? + +This package allows you to use `queueMicrotask` safely in all modern JS engines. Use it if you prioritize small JS bundle size over support for old browsers. + +If you just need to support Node 12 and later, use `queueMicrotask` directly. If you need to support all versions of Node, use this package. + +## Why not use `process.nextTick`? + +In Node, `queueMicrotask` and `process.nextTick` are [essentially equivalent](https://nodejs.org/api/globals.html#globals_queuemicrotask_callback), though there are [subtle differences](https://github.com/YuzuJS/setImmediate#macrotasks-and-microtasks) that don't matter in most situations. + +You can think of `queueMicrotask` as a standardized version of `process.nextTick` that works in the browser. No need to rely on your browser bundler to shim `process` for the browser environment. + +## Why not use `setTimeout(fn, 0)`? + +This approach is the most compatible, but it has problems. Modern browsers throttle timers severely, so `setTimeout(…, 0)` usually takes at least 4ms to run. Furthermore, the throttling gets even worse if the page is backgrounded. If you have many `setTimeout` calls, then this can severely limit the performance of your program. + +## Why not use a microtask library like [`immediate`](https://www.npmjs.com/package/immediate) or [`asap`](https://www.npmjs.com/package/asap)? + +These packages are great! However, if you prioritize small JS bundle size over optimal performance in old browsers then you may want to consider this package. + +This package (`queue-microtask`) is four times smaller than `immediate`, twice as small as `asap`, and twice as small as using `process.nextTick` and letting the browser bundler shim it automatically. + +Note: This package throws an exception in JS environments which lack `Promise` support -- which are usually very old browsers and Node.js versions. + +Since the `queueMicrotask` API is supported in Node.js, Chrome, Firefox, Safari, Opera, and Edge, **the vast majority of users will get optimal performance**. Any JS environment with `Promise`, which is almost all of them, also get optimal performance. If you need support for JS environments which lack `Promise` support, use one of the alternative packages. + +## What is a shim? + +> In computer programming, a shim is a library that transparently intercepts API calls and changes the arguments passed, handles the operation itself or redirects the operation elsewhere. – [Wikipedia](https://en.wikipedia.org/wiki/Shim_(computing)) + +This package could also be described as a "ponyfill". + +> A ponyfill is almost the same as a polyfill, but not quite. Instead of patching functionality for older browsers, a ponyfill provides that functionality as a standalone module you can use. – [PonyFoo](https://ponyfoo.com/articles/polyfills-or-ponyfills) + +## API + +### `queueMicrotask(fn)` + +The `queueMicrotask()` method queues a microtask. + +The `fn` argument is a function to be executed after all pending tasks have completed but before yielding control to the browser's event loop. + +## license + +MIT. Copyright (c) [Feross Aboukhadijeh](https://feross.org). diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/index.d.ts b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/index.d.ts new file mode 100644 index 0000000..b6a8646 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/index.d.ts @@ -0,0 +1,2 @@ +declare const queueMicrotask: (cb: () => void) => void +export = queueMicrotask diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/index.js new file mode 100644 index 0000000..5560534 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/index.js @@ -0,0 +1,9 @@ +/*! queue-microtask. MIT License. Feross Aboukhadijeh */ +let promise + +module.exports = typeof queueMicrotask === 'function' + ? queueMicrotask.bind(typeof window !== 'undefined' ? window : global) + // reuse resolved promise, and allocate it lazily + : cb => (promise || (promise = Promise.resolve())) + .then(cb) + .catch(err => setTimeout(() => { throw err }, 0)) diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/package.json new file mode 100644 index 0000000..d29a401 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/queue-microtask/package.json @@ -0,0 +1,55 @@ +{ + "name": "queue-microtask", + "description": "fast, tiny `queueMicrotask` shim for modern engines", + "version": "1.2.3", + "author": { + "name": "Feross Aboukhadijeh", + "email": "feross@feross.org", + "url": "https://feross.org" + }, + "bugs": { + "url": "https://github.com/feross/queue-microtask/issues" + }, + "devDependencies": { + "standard": "*", + "tape": "^5.2.2" + }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "homepage": "https://github.com/feross/queue-microtask", + "keywords": [ + "asap", + "immediate", + "micro task", + "microtask", + "nextTick", + "process.nextTick", + "queue micro task", + "queue microtask", + "queue-microtask", + "queueMicrotask", + "setImmediate", + "task" + ], + "license": "MIT", + "main": "index.js", + "repository": { + "type": "git", + "url": "git://github.com/feross/queue-microtask.git" + }, + "scripts": { + "test": "standard && tape test/*.js" + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/run-parallel-limit/LICENSE b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/run-parallel-limit/LICENSE new file mode 100644 index 0000000..c7e6852 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/run-parallel-limit/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) Feross Aboukhadijeh + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/run-parallel-limit/README.md b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/run-parallel-limit/README.md new file mode 100644 index 0000000..6f966b3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/run-parallel-limit/README.md @@ -0,0 +1,94 @@ +# run-parallel-limit [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url] + +[travis-image]: https://img.shields.io/travis/feross/run-parallel-limit/master.svg +[travis-url]: https://travis-ci.org/feross/run-parallel-limit +[npm-image]: https://img.shields.io/npm/v/run-parallel-limit.svg +[npm-url]: https://npmjs.org/package/run-parallel-limit +[downloads-image]: https://img.shields.io/npm/dm/run-parallel-limit.svg +[downloads-url]: https://npmjs.org/package/run-parallel-limit +[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg +[standard-url]: https://standardjs.com + +### Run an array of functions in parallel, but limit the number of tasks executing at the same time + +![run-parallel-limit](img.png) [![Sauce Test Status](https://saucelabs.com/browser-matrix/run-parallel-limit.svg)](https://saucelabs.com/u/run-parallel-limit) + +### install + +``` +npm install run-parallel-limit +``` + +### usage + +#### parallelLimit(tasks, limit, [callback]) + +Run the `tasks` array of functions in parallel, with a maximum of `limit` tasks executing +at the same time. If any of the functions pass an error to its callback, the main +`callback` is immediately called with the value of the error. Once the `tasks` have +completed, the results are passed to the final `callback` as an array. + +Note that the `tasks` are not executed in batches, so there is no guarantee that the first +`limit` tasks will complete before any others are started. + +It is also possible to use an object instead of an array. Each property will be run as a +function and the results will be passed to the final `callback` as an object instead of +an array. This can be a more readable way of handling the results. + +##### arguments + +- `tasks` - An array or object containing functions to run. Each function is passed a +`callback(err, result)` which it must call on completion with an error `err` (which can +be `null`) and an optional `result` value. +- `limit` - The maximum number of `tasks` to run at any time. +- `callback(err, results)` - An optional callback to run once all the functions have +completed. This function gets a results array (or object) containing all the result +arguments passed to the task callbacks. + +##### example + +```js +var parallelLimit = require('run-parallel-limit') + +var tasks = [ + function (callback) { + setTimeout(function () { + callback(null, 'one') + }, 200) + }, + function (callback) { + setTimeout(function () { + callback(null, 'two') + }, 100) + }, + ... hundreds more tasks ... +] + +parallelLimit(tasks, 5, function (err, results) { + // optional callback + // the results array will equal ['one', 'two', ...] even though + // the second function had a shorter timeout. +}) +``` + +The above code runs with a concurrency `limit` of 5, so at most 5 tasks will be running at +any given time. + +This module is basically equavalent to +[`async.parallelLimit`](https://github.com/caolan/async#parallellimittasks-limit-callback), +but it's handy to just have the one function you need instead of the kitchen sink. +Modularity! Especially handy if you're serving to the browser and need to reduce your +javascript bundle size. + +Works great in the browser with [browserify](http://browserify.org/)! + +### see also + +- [run-auto](https://github.com/feross/run-auto) +- [run-parallel](https://github.com/feross/run-parallel) +- [run-series](https://github.com/feross/run-series) +- [run-waterfall](https://github.com/feross/run-waterfall) + +### license + +MIT. Copyright (c) [Feross Aboukhadijeh](http://feross.org). diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/run-parallel-limit/index.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/run-parallel-limit/index.js new file mode 100644 index 0000000..5501db2 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/run-parallel-limit/index.js @@ -0,0 +1,70 @@ +/*! run-parallel-limit. MIT License. Feross Aboukhadijeh */ +module.exports = runParallelLimit + +const queueMicrotask = require('queue-microtask') + +function runParallelLimit (tasks, limit, cb) { + if (typeof limit !== 'number') throw new Error('second argument must be a Number') + let results, len, pending, keys, isErrored + let isSync = true + let next + + if (Array.isArray(tasks)) { + results = [] + pending = len = tasks.length + } else { + keys = Object.keys(tasks) + results = {} + pending = len = keys.length + } + + function done (err) { + function end () { + if (cb) cb(err, results) + cb = null + } + if (isSync) queueMicrotask(end) + else end() + } + + function each (i, err, result) { + results[i] = result + if (err) isErrored = true + if (--pending === 0 || err) { + done(err) + } else if (!isErrored && next < len) { + let key + if (keys) { + key = keys[next] + next += 1 + tasks[key](function (err, result) { each(key, err, result) }) + } else { + key = next + next += 1 + tasks[key](function (err, result) { each(key, err, result) }) + } + } + } + + next = limit + if (!pending) { + // empty + done(null) + } else if (keys) { + // object + keys.some(function (key, i) { + tasks[key](function (err, result) { each(key, err, result) }) + if (i === limit - 1) return true // early return + return false + }) + } else { + // array + tasks.some(function (task, i) { + task(function (err, result) { each(i, err, result) }) + if (i === limit - 1) return true // early return + return false + }) + } + + isSync = false +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/run-parallel-limit/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/run-parallel-limit/package.json new file mode 100644 index 0000000..fed74f5 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/node_modules/run-parallel-limit/package.json @@ -0,0 +1,64 @@ +{ + "name": "run-parallel-limit", + "description": "Run an array of functions in parallel, but limit the number of tasks executing at the same time", + "version": "1.1.0", + "author": { + "name": "Feross Aboukhadijeh", + "email": "feross@feross.org", + "url": "https://feross.org" + }, + "bugs": { + "url": "https://github.com/feross/run-parallel-limit/issues" + }, + "dependencies": { + "queue-microtask": "^1.2.2" + }, + "devDependencies": { + "airtap": "^3.0.0", + "standard": "*", + "tape": "^5.1.1" + }, + "homepage": "https://github.com/feross/run-parallel-limit", + "keywords": [ + "parallel", + "async", + "function", + "callback", + "asynchronous", + "run", + "array", + "run parallel", + "limit", + "max", + "queue", + "run parallel limit", + "max tasks", + "tasks" + ], + "license": "MIT", + "main": "index.js", + "repository": { + "type": "git", + "url": "git://github.com/feross/run-parallel-limit.git" + }, + "scripts": { + "test": "standard && npm run test-node && npm run test-browser", + "test-browser": "airtap -- test/*.js", + "test-browser-local": "airtap --local -- test/*.js", + "test-node": "tape test/*.js" + }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/package.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/package.json new file mode 100644 index 0000000..3055feb --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "level": "^8.0.0" + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/preload.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/preload.js new file mode 100644 index 0000000..cb1a5ba --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/preload.js @@ -0,0 +1,21 @@ +const { contextBridge, ipcRenderer } = require("electron"); + +contextBridge.exposeInMainWorld("anti_recall", { + clearDb: () => ipcRenderer.invoke("LiteLoader.anti_recall.clearDb"), + getNowConfig: () => + ipcRenderer.invoke("LiteLoader.anti_recall.getNowConfig"), + saveConfig: (config) => + ipcRenderer.invoke("LiteLoader.anti_recall.saveConfig", config), + repatchCss: (callback) => + ipcRenderer.on( + "LiteLoader.anti_recall.mainWindow.repatchCss", + callback + ), + recallTip: (callback) => + ipcRenderer.on("LiteLoader.anti_recall.mainWindow.recallTip", callback), + recallTipList: (callback) => + ipcRenderer.on( + "LiteLoader.anti_recall.mainWindow.recallTipList", + callback + ) +}); diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/renderer.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/renderer.js new file mode 100644 index 0000000..74f39db --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/plugins/qq-anti-recall/renderer.js @@ -0,0 +1,676 @@ +var recalledMsgList = []; + +var nowConfig = {}; + +export async function onSettingWindowCreated(view) { + nowConfig = await window.anti_recall.getNowConfig(); + + const new_navbar_item = ` + +
+
+

主配置

+
+ +
+
+

操作

+
+ +
+
+
+
+
+

是否将撤回消息存入数据库

+ 数据库不加密,若开启风险自负;若不开启,重启QQ后撤回消息会丢失;开启选项后,反撤回消息才会开始保存;若之前开过,现在关闭,储存的消息不会被删除,需要你手动清理。 +
+
+ +
+
+ +
+
+
+

是否反撤回自己的消息

+ 如果开启,则自己发送的消息也会被反撤回。开启后,从下一条消息开始起生效。 +
+
+ +
+
+ +
+ +
+
+

内存中消息最多缓存条数

+ 修改将自动保存并立即生效;如果过少可能导致消息接受太快时来不及反撤回,如果过多可能导致内存占用过高。 +
+
+ 条 +
+
+ +
+
+

清理内存缓存消息时一次性清理多少

+ 修改将自动保存并立即生效;一次性清理过多可能导致某些消息反撤回失败,过少则可能导致内存增长过快。 +
+
+ 条 +
+
+ + +
+ +
+
+ +
+

样式配置

+
+ +
+ +
+
+

撤回主题色

+ 将会同时影响阴影和“已撤回”提示的颜色 +
+
+ +
+
+ +
+ +
+
+

撤回后消息是否显示阴影

+ 修改将自动保存并实时生效 +
+
+ +
+
+ +
+ +
+
+

撤回后消息下方是否显示“已撤回”提示

+ 修改将自动保存并在重新滚动消息后生效 +
+
+ +
+
+ +
+ +
+
+ + +
+ + `; + + const parser = new DOMParser(); + + const doc2 = parser.parseFromString(new_navbar_item, "text/html"); + const node2 = doc2.querySelector("body > div"); + + //清空消息 + node2.querySelector("#clearDb").onclick = async () => { + await window.anti_recall.clearDb(); + }; + + node2.querySelector("#maxMsgLimit").onblur = async () => { + var limit = parseFloat(node2.querySelector("#maxMsgLimit").value); + if (limit <= 0 || limit > 99999999) { + alert("你的数量输入有误!将不会保存,请重新输入"); + return; + } + nowConfig.maxMsgSaveLimit = limit; + await window.anti_recall.saveConfig(nowConfig); + }; + + node2.querySelector("#deletePerTime").onblur = async () => { + var limit = parseFloat(node2.querySelector("#deletePerTime").value); + if (limit <= 0 || limit > 99999) { + alert("你的数量输入有误!将不会保存,请重新输入"); + return; + } + nowConfig.deleteMsgCountPerTime = limit; + await window.anti_recall.saveConfig(nowConfig); + }; + + //选择颜色 + const pickColor = node2.querySelector(".pick-color"); + pickColor.value = nowConfig.mainColor; + pickColor.addEventListener("change", async (event) => { + nowConfig.mainColor = event.target.value; + await window.anti_recall.saveConfig(nowConfig); + }); + + //存数据库开关 + var q_switch_savedb = node2.querySelector("#switchSaveDb"); + + if (nowConfig.saveDb == null || nowConfig.saveDb == true) { + q_switch_savedb.classList.toggle("is-active"); + } + + q_switch_savedb.addEventListener("click", async () => { + if (q_switch_savedb.classList.contains("is-active")) { + nowConfig.saveDb = false; + } else { + nowConfig.saveDb = true; + } + q_switch_savedb.classList.toggle("is-active"); + await window.anti_recall.saveConfig(nowConfig); + }); + + //反撤回自己消息开关 + var q_switch_antiself = node2.querySelector("#switchAntiRecallSelf"); + + if (nowConfig.isAntiRecallSelfMsg == true) { + q_switch_antiself.classList.toggle("is-active"); + } + + q_switch_antiself.addEventListener("click", async () => { + if (q_switch_antiself.classList.contains("is-active")) { + nowConfig.isAntiRecallSelfMsg = false; + } else { + nowConfig.isAntiRecallSelfMsg = true; + } + q_switch_antiself.classList.toggle("is-active"); + await window.anti_recall.saveConfig(nowConfig); + }); + + //阴影开关 + var q_switch_shadow = node2.querySelector("#switchShadow"); + + if (nowConfig.enableShadow == null || nowConfig.enableShadow == true) { + q_switch_shadow.classList.toggle("is-active"); + } + + q_switch_shadow.addEventListener("click", async () => { + if (q_switch_shadow.classList.contains("is-active")) { + nowConfig.enableShadow = false; + } else { + nowConfig.enableShadow = true; + } + q_switch_shadow.classList.toggle("is-active"); + await window.anti_recall.saveConfig(nowConfig); + }); + + //提示开关 + var q_switch_tip = node2.querySelector("#switchTip"); + + if (nowConfig.enableTip == null || nowConfig.enableTip == true) { + q_switch_tip.classList.toggle("is-active"); + } + + q_switch_tip.addEventListener("click", async () => { + if (q_switch_tip.classList.contains("is-active")) { + //取消 + nowConfig.enableTip = false; + } else { + //重新设置 + nowConfig.enableTip = true; + } + q_switch_tip.classList.toggle("is-active"); + await window.anti_recall.saveConfig(nowConfig); + }); + + view.appendChild(node2); +} + +async function patchCss() { + nowConfig = await window.anti_recall.getNowConfig(); + + var cssNode = document + .evaluate("/html/head/style[@id='anti-recall-css']", document) + .iterateNext(); + if (cssNode) { + cssNode.parentElement.removeChild(cssNode); + } + var stylee = document.createElement("style"); + stylee.type = "text/css"; + stylee.id = "anti-recall-css"; + var sHtml = `.message-content-recalled-parent { + border-radius: 10px; + position: relative; + overflow: unset !important;`; + if (nowConfig.enableShadow == true) { + sHtml += ` margin-top:3px; + margin-left:3px; + margin-right:3px; + margin-bottom: 25px; + box-shadow: 0px 0px 20px 5px ${nowConfig.mainColor};`; + } else { + sHtml += `margin-bottom: 15px;`; + } + sHtml += ` } + .recalledNoMargin { + margin-top: 0px!important; + } + + .message-content-recalled { + position: absolute; + top: calc(100% + 6px); + font-size: 12px; + white-space: nowrap; + color: var(--text-color); + left: 0; + background-color: var(--background-color-05); + backdrop-filter: blur(28px); + padding: 4px 8px; + margin-bottom: 2px; + border-radius: 6px; + box-shadow: var(--box-shadow); + transition: 300ms; + transform: translateX(-30%); + opacity: 0; + pointer-events: none; + color:${nowConfig.mainColor}; + } + `; + stylee.innerHTML = sHtml; + document.getElementsByTagName("head").item(0).appendChild(stylee); +} + +onLoad(); + +async function onLoad() { + anti_recall.repatchCss(async (event, _) => { + await patchCss(); + }); + + //消息更新回调 + anti_recall.recallTip(async (event, msgId) => { + console.log("[Anti-Recall]", "尝试反撤回消息ID", msgId); + + var oldElement = document.getElementById(`${msgId}-msgContainerMsgContent`); + + var newElement = document.getElementById(`${msgId}-msgContent`); + + var unixElement = document + .getElementById(`ml-${msgId}`) + ?.querySelector(".msg-content-container"); + + var cardElement = document.getElementById(`${msgId}-msgContent`); + + var arkElement = document.getElementById( + `ark-msg-content-container_${msgId}` + ); + + if (oldElement != null) { + if (oldElement.classList.contains("gray-tip-message")) return; + await appendRecalledTag(oldElement); + } else if (newElement != null) { + if (newElement.classList.contains("gray-tip-message")) return; + await appendRecalledTag(newElement.parentElement); + } else if (unixElement != null) { + if (unixElement.classList.contains("gray-tip-message")) return; + await appendRecalledTag(unixElement.parentElement); + } else if (cardElement != null) { + if (cardElement.classList.contains("gray-tip-message")) return; + cardElement.classList.add("recalledNoMargin"); + await appendRecalledTag(cardElement.parentElement); + } else if (arkElement != null) { + if (arkElement.classList.contains("gray-tip-message")) return; + arkElement.classList.add("recalledNoMargin"); + await appendRecalledTag(arkElement.parentElement); + } else{ + var container = document.querySelector(`.ml-item[id='${msgId}'] .msg-content-container`); + if (container) await appendRecalledTag(container); + } + }); + //消息列表更新回调 + anti_recall.recallTipList(async (event, msgIdList) => { + recalledMsgList = msgIdList; + await render(); + }); + + await patchCss(); + + var observerRendering = false; + //监控消息列表,如果有撤回则渲染 + const observer = new MutationObserver(async (mutationsList) => { + for (let mutation of mutationsList) { + if (mutation.type === "childList") { + if ( + mutation.addedNodes != null && + mutation.addedNodes.length > 0 && + mutation.addedNodes[0].classList != null && + mutation.addedNodes[0].classList.contains("message-content-recalled") + ) { + //是添加的撤回标记,直接忽略 + } else { + if (observerRendering) continue; + observerRendering = true; + setTimeout(() => { + observerRendering = false; + render(); + }, 50); + } + } + } + }); + + var finder = setInterval(() => { + if (document.querySelector(".ml-list.list")) { + clearInterval(finder); + console.log("[Anti-Recall]", "检测到聊天区域,已在当前页面加载反撤回"); + const targetNode = document.querySelector(".ml-list.list"); + const config = { + attributes: false, + childList: true, + subtree: true, + }; + observer.observe(targetNode, config); + } + }, 100); + + async function render() { + // console.log("[Anti-Recall]", "尝试反撤回消息列表", recalledMsgList); + + var elements = document + .querySelector(".chat-msg-area__vlist") + ?.querySelectorAll(".ml-item"); + + nowConfig = await window.anti_recall.getNowConfig(); + + for (var el of elements) { + var findMsgId = recalledMsgList.find((i) => i == el.id); + if (findMsgId != null) { + var msgId = findMsgId; + try { + var oldElement = el.querySelector( + `div[id='${msgId}-msgContainerMsgContent']` + ); + + var newElement = el.querySelector(`div[id='${msgId}-msgContent']`); + + var unixElement = el + .querySelector(`div[id='ml-${msgId}']`) + ?.querySelector(".msg-content-container"); + + var cardElement = el.querySelector(`div[id='${msgId}-msgContent']`); + + var arkElement = el.querySelector( + `div[id='ark-msg-content-container_${msgId}']` + ); + + if (oldElement != null) { + if (oldElement.classList.contains("gray-tip-message")) continue; + await appendRecalledTag(oldElement); + } else if (newElement != null) { + if (newElement.classList.contains("gray-tip-message")) continue; + await appendRecalledTag(newElement.parentElement); + } else if (unixElement != null) { + if (unixElement.classList.contains("gray-tip-message")) continue; + await appendRecalledTag(unixElement.parentElement); + } else if (cardElement != null) { + if (cardElement.classList.contains("gray-tip-message")) continue; + cardElement.classList.add("recalledNoMargin"); + await appendRecalledTag(cardElement.parentElement); + } else if (arkElement != null) { + if (arkElement.classList.contains("gray-tip-message")) continue; + arkElement.classList.add("recalledNoMargin"); + await appendRecalledTag(arkElement.parentElement); + } + else{ + var container = el.querySelector('.msg-content-container'); + if (container) await appendRecalledTag(container); + } + } catch (e) { + console.log("[Anti-Recall]", "反撤回消息时出错", e); + } + } + } + } + + async function appendRecalledTag(msgElement) { + if (!msgElement) return; + + var currRecalledTip = msgElement.querySelector(".message-content-recalled"); + if (currRecalledTip == null) { + msgElement.classList.add("message-content-recalled-parent"); + + if (nowConfig.enableTip == true) { + const recalledEl = document.createElement("div"); + recalledEl.innerText = "已撤回"; + recalledEl.classList.add("message-content-recalled"); + + msgElement.appendChild(recalledEl); + setTimeout(() => { + recalledEl.style.transform = "translateX(0)"; + recalledEl.style.opacity = "1"; + }, 5); + } + } else { + //已经有撤回标记了,不再重复添加 + } + } +} diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/components/renderer.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/components/renderer.js new file mode 100644 index 0000000..7dc5409 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/components/renderer.js @@ -0,0 +1,221 @@ +import style from "./static/style.css" assert { type: "css" }; + +const html_url = "local://root/src/components/static/template.html"; +const html_file = await (await fetch(html_url)).text(); +const template = new DOMParser().parseFromString(html_file, "text/html"); + + +Object.defineProperty(globalThis, "SettingElementStyleSheets", { + value: new class { + #styleSheets = []; + #callbacks = []; + set styleSheets(value) { + this.#styleSheets = value; + for (const callback of this.#callbacks) { + callback(this.#styleSheets); + } + } + get styleSheets() { + return this.#styleSheets; + } + on(callback) { + this.#callbacks.push(callback); + callback(this.#styleSheets); + } + } +}); + + +class SettingElementBase extends HTMLElement { + constructor(element_id) { + super(); + this.attachShadow({ mode: "open" }); + this._template = template.getElementById(element_id); + this._content = this._template.content.cloneNode(true); + this._slot = this.shadowRoot.querySelector("slot"); + this.shadowRoot.append(this._content); + SettingElementStyleSheets.on((styleSheets) => { + this.shadowRoot.adoptedStyleSheets = styleSheets; + }); + } + attributeChangedCallback() { + this.update(); + } + update() { + return; + } +} + + +SettingElementStyleSheets.styleSheets = [style]; + + +customElements.define("setting-section", class extends SettingElementBase { + static observedAttributes = ["data-title"]; + constructor() { + super("setting-section"); + this._title = this.shadowRoot.querySelector("h1"); + this.update(); + } + update() { + this._title.textContent = this.dataset["title"]; + } +}); + + +customElements.define("setting-panel", class extends SettingElementBase { + constructor() { + super("setting-panel"); + } +}); + + +customElements.define("setting-list", class extends SettingElementBase { + static observedAttributes = ["data-title", "data-direction", "is-collapsible", "is-active", "is-disabled"]; + constructor() { + super("setting-list"); + this._head = this.shadowRoot.querySelector("setting-item"); + this._title = this.shadowRoot.querySelector("h2"); + this._slot = this.shadowRoot.querySelector("slot"); + this._head.addEventListener("click", () => { + this.toggleAttribute("is-active"); + }); + this.update(); + new MutationObserver((_, observer) => { + observer.disconnect(); + this.update(); + observer.observe(this, { childList: true }); + }).observe(this, { childList: true }); + } + update() { + this._title.textContent = this.dataset["title"]; + const slot_children = this._slot.assignedElements(); + this.querySelectorAll("setting-divider").forEach(node => node.remove()); + // 折叠列表 + if (this.hasAttribute("is-collapsible")) { + this._head.classList.toggle("hidden", false); + slot_children.forEach((node, index) => { + const setting_divider = document.createElement("setting-divider"); + if (this.dataset["direction"] == "column") { + setting_divider.dataset["direction"] = "row"; + node.dataset["direction"] = "row"; + } + if (index < slot_children.length) { + node.before(setting_divider); + } + }); + } + // 普通列表 + else { + this._head.classList.toggle("hidden", true); + slot_children.forEach((node, index) => { + const setting_divider = document.createElement("setting-divider"); + if (this.dataset["direction"] == "column") { + setting_divider.dataset["direction"] = "row"; + node.dataset["direction"] = "row"; + } + if (this.dataset["direction"] == "row") { + setting_divider.dataset["direction"] = "column"; + node.dataset["direction"] = "column"; + } + if (index + 1 < slot_children.length) { + node.after(setting_divider); + } + }); + } + } +}); + + +customElements.define("setting-item", class extends SettingElementBase { + static observedAttributes = ["data-direction"]; + constructor() { + super("setting-item"); + } +}); + + +customElements.define("setting-select", class extends SettingElementBase { + static observedAttributes = ["is-disabled"]; + constructor() { + super("setting-select"); + this._title = this.shadowRoot.querySelector("input"); + this._button = this.shadowRoot.querySelector(".menu-button"); + this._context = this.shadowRoot.querySelector("ul"); + const click = () => { + this._context.classList.toggle("hidden"); + if (!this._context.classList.contains("hidden")) { + window.addEventListener("pointerup", pointerup); + this._context.style.width = getComputedStyle(this).getPropertyValue("width"); + } + else { + window.removeEventListener("pointerup", pointerup); + this._context.style.width = null; + } + } + const pointerup = (event) => { + if (event.target.tagName != "SETTING-SELECT") { + click(); + } + } + this._button.addEventListener("click", click); + this._context.addEventListener("click", (event) => { + if (event.target.tagName == "SETTING-OPTION" && !event.target.hasAttribute("is-selected")) { + for (const node of this.querySelectorAll("setting-option[is-selected]")) { + node.toggleAttribute("is-selected"); + } + event.target.toggleAttribute("is-selected"); + this._title.value = event.target.textContent; + this.dispatchEvent(new CustomEvent("selected", { + bubbles: true, + composed: true, + detail: { + name: event.target.textContent, + value: event.target.dataset["value"] + } + })); + } + }); + this._title.value = this.querySelector("setting-option[is-selected]")?.textContent; + } +}); + + +customElements.define("setting-option", class extends SettingElementBase { + static observedAttributes = ["data-value", "is-selected", "is-disabled"]; + constructor() { + super("setting-option"); + } +}); + + +customElements.define("setting-switch", class extends SettingElementBase { + static observedAttributes = ["is-active", "is-disabled"]; + constructor() { + super("setting-switch"); + } +}); + + +customElements.define("setting-button", class extends SettingElementBase { + static observedAttributes = ["data-type", "is-disabled"]; + constructor() { + super("setting-button"); + } +}); + + +customElements.define("setting-text", class extends SettingElementBase { + static observedAttributes = ["data-type"]; + constructor() { + super("setting-text"); + } +}); + + +customElements.define("setting-divider", class extends SettingElementBase { + static observedAttributes = ["data-direction"]; + constructor() { + super("setting-divider"); + } +}); \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/components/static/style.css b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/components/static/style.css new file mode 100644 index 0000000..5cc34ef --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/components/static/style.css @@ -0,0 +1,383 @@ +@scope (:host) { + :scope[is-disabled] { + opacity: 0.3; + cursor: not-allowed; + pointer-events: none; + } + + :scope .hidden { + display: none !important; + } +} + + +@scope (:host(setting-section)) { + :scope h1 { + color: var(--text_primary); + font-weight: var(--font-bold); + font-size: min(var(--font_size_3), 18px); + line-height: min(var(--line_height_3), 24px); + padding: 0px 16px; + margin-top: 0; + margin-bottom: 8px; + } +} + + +@scope (:host(setting-panel)) { + :scope { + display: block; + background-color: var(--fill_light_primary, var(--fg_white)); + border-radius: 8px; + font-size: min(var(--font_size_3), 18px); + line-height: min(var(--line_height_3), 24px); + margin-bottom: 20px; + overflow: hidden; + } +} + + +/* what the fuck */ +@scope (:host(setting-list)) { + :scope[data-direction="column"] { + display: block; + padding: 0px 16px; + } + + :scope[data-direction="row"] { + display: flex; + justify-content: space-between; + padding: 16px 0px; + } + + :scope svg { + width: 1rem; + height: 1rem; + transform: rotate(0deg); + transition-duration: 0.2s; + transition-timing-function: ease; + transition-delay: 0s; + transition-property: transform; + } + + :scope[is-collapsible] slot { + display: none !important; + } + + /* 不理解下面两段是怎么回事,这两段都是有用的 */ + :scope[is-active] slot { + display: block !important; + } + + /* 但是需要在下面写其中一段,才能让这两段有用 */ + :scope[is-active] svg { + transform: rotate(-180deg); + } + + :scope setting-item { + cursor: pointer; + font-size: min(var(--font_size_3), 18px); + line-height: min(var(--line_height_3), 24px); + } + + :scope h2 { + box-sizing: border-box; + font-size: 100%; + font-style: inherit; + font-weight: inherit; + border: 0px; + margin: 0px; + padding: 0px; + } +} + +/* 这段虽然看似没用,但是不可以删掉,可以替换成另一个,让上面两个有用 */ +:host(setting-list[is-active]) svg { + transform: rotate(-180deg); +} + + +@scope (:host(setting-item)) { + :scope[data-direction="column"] { + flex: 1; + margin: 0px 10px; + text-align: center; + } + + :scope[data-direction="row"] { + margin: 12px 0px; + display: flex; + justify-content: space-between; + align-items: center; + } +} + + +@scope (:host(setting-select)) { + :scope { + display: block; + width: 100px; + } + + :scope .select { + width: 100%; + color: var(--text_primary); + font-size: 12px; + position: relative; + z-index: inherit; + } + + :scope .menu-button { + width: 100%; + height: 24px; + line-height: 24px; + padding: 0px 8px; + background-color: transparent; + border-radius: 4px; + cursor: default; + display: flex; + justify-content: space-between; + position: relative; + z-index: 5; + box-sizing: border-box; + border: 1px solid var(--border_dark); + } + + :scope input { + appearance: none; + border-radius: 0px; + box-sizing: border-box; + background: none; + border: none; + padding: 0px; + cursor: default; + background-color: transparent; + color: var(--text_primary); + flex: 1; + margin-right: 8px; + outline: none; + overflow: hidden; + text-overflow: ellipsis; + } + + :scope svg { + width: 16px; + height: 16px; + color: var(--icon_primary); + position: relative; + top: 3px; + } + + :scope ul { + position: absolute; + top: 100%; + backdrop-filter: blur(8px); + display: flex; + flex-direction: column; + gap: 4px; + list-style: none; + font-size: 12px; + background-color: var(--blur_middle_standard); + background-clip: padding-box; + border-radius: 4px; + box-shadow: var(--shadow_bg_middle_secondary); + border: var(--border_secondary); + padding: 4px; + app-region: no-drag; + box-sizing: border-box; + max-height: var(--q-contextmenu-max-height); + overflow-x: hidden; + overflow-y: auto; + margin: 5px 0px; + z-index: 999; + } +} + + +@scope (:host(setting-option)) { + :scope { + display: block; + } + + :scope li { + & { + display: flex; + justify-content: space-between; + border-radius: 4px; + box-sizing: border-box; + line-height: 24px; + color: var(--text_primary); + font-size: 12px; + padding: 0px 8px; + } + + &:hover { + background-color: var(--overlay_hover); + } + + &:active { + background-color: var(--overlay_pressed); + } + } + + + :scope[is-selected] li { + background-color: var(--overlay_active); + } + + :scope span { + margin-right: 8px; + overflow: hidden; + text-wrap: nowrap; + text-overflow: ellipsis; + } + + :scope svg { + flex-shrink: 0; + display: none; + width: 1em; + height: 1em; + color: var(--icon_primary); + position: relative; + right: -4px; + top: 7px; + } + + :scope[is-selected] svg { + display: block; + } +} + + +@scope (:host(setting-switch)) { + :scope { + background-color: var(--fill_standard_primary); + border-radius: 14px; + box-sizing: border-box; + display: inline-flex; + position: relative; + transition-duration: 0.2s; + transition-timing-function: cubic-bezier(0.38, 0, 0.24, 1); + transition-delay: 0s; + transition-property: all; + width: 28px; + padding: 3px; + } + + :scope[is-active] { + background-color: var(--brand_standard); + } + + :scope span { + border-radius: 5px; + box-shadow: rgba(0, 0, 0, 0.09) 0px 2px 4px; + box-sizing: border-box; + display: inline-block; + height: 10px; + position: relative; + transition-duration: 0.2s; + transition-timing-function: cubic-bezier(0.38, 0, 0.24, 1); + transition-delay: 0s; + transition-property: all; + width: 10px; + z-index: 2; + background: var(--icon_white); + } + + :scope[is-active] span { + transform: translate(12px); + } +} + + +@scope (:host(setting-button)) { + :scope { + position: relative; + vertical-align: text-bottom; + } + + :scope button { + display: inline-flex; + align-items: center; + justify-content: center; + overflow: visible; + background-color: var(--brand_standard); + border: 1px solid; + border-radius: 4px; + outline-style: none; + font-size: 12px; + line-height: 14px; + min-width: 62px; + margin: 0px; + padding: 4px 7px; + } + + :scope[data-type="primary"] button { + & { + background-color: var(--brand_standard); + color: var(--on_brand_primary); + border-color: var(--brand_standard); + } + + &:hover { + background: var(--nt_brand_standard_2_overlay_hover_brand_2_mix); + } + + &:active { + background-color: var(--nt_brand_standard_2_overlay_pressed_brand_2_mix); + } + } + + :scope[data-type="secondary"] button { + & { + background-color: transparent; + color: var(--text_primary); + border-color: var(--fill_standard_primary); + } + + &:hover { + background-color: var(--overlay_hover); + } + + &:active { + background-color: var(--overlay_pressed); + } + } +} + + +@scope (:host(setting-text)) { + :scope { + display: -webkit-box; + word-break: break-all; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + overflow: hidden; + } + + :scope[data-type="secondary"] slot { + color: var(--text_secondary); + font-size: min(var(--font_size_2), 16px); + line-height: min(var(--line_height_2), 22px); + margin-top: 4px; + } +} + + +@scope (:host(setting-divider)) { + :scope { + display: block; + border: unset; + margin: unset; + background-color: rgba(127, 127, 127, 0.15); + } + + :scope[data-direction="row"] { + height: 1px; + } + + :scope[data-direction="column"] { + width: 1px; + } +} \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/components/static/template.html b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/components/static/template.html new file mode 100644 index 0000000..a2591f9 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/components/static/template.html @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/easter_eggs/check_update.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/easter_eggs/check_update.js new file mode 100644 index 0000000..022d5d8 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/easter_eggs/check_update.js @@ -0,0 +1,18 @@ +export const hash = "#/setting"; +export const selector = ".config_view .versions .new setting-button"; +export function trigger(check_update_button) { + let click_count = 0; + check_update_button.addEventListener("click", () => { + click_count++; + if (click_count == 20) { + click_count = 0; + new Notification( + "LiteLoaderQQNT", + { + body: "你咋这么急着更新?\n你就不能再等等?\n或者来催一下沫烬染(", + requireInteraction: true + } + ); + } + }); +} \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/easter_eggs/renderer.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/easter_eggs/renderer.js new file mode 100644 index 0000000..dab12c1 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/easter_eggs/renderer.js @@ -0,0 +1,52 @@ +// 寻找指定元素 +async function findElement(selector, callback) { + const observer = (_, observer) => { + const element = document.querySelector(selector); + if (element) { + callback(element); + observer?.disconnect?.(); + return true; + } + return false; + } + if (!observer()) { + new MutationObserver(observer).observe(document, { + subtree: true, + attributes: false, + childList: true + }); + } +} + + +// 监听页面变化 +async function watchURLHash(callback) { + if (!location.hash.includes("#/blank")) { + callback(location.hash); + } + else { + navigation.addEventListener("navigatesuccess", () => { + callback(location.hash) + }, { once: true }); + } +} + + +// 加载彩蛋 +async function loadEasterEggs(currentHash) { + const easter_eggs = [ + "./check_update.js", + "./search_furry.js", + "./setting_navtab.js" + ]; + for (const easter_egg of easter_eggs) { + const { hash, selector, trigger } = await import(easter_egg); + if (currentHash.includes(hash)) { + findElement(selector, trigger); + } + } +} + + +// 指定页面触发 +watchURLHash(loadEasterEggs); \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/easter_eggs/search_furry.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/easter_eggs/search_furry.js new file mode 100644 index 0000000..a376d5e --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/easter_eggs/search_furry.js @@ -0,0 +1,38 @@ +export const hash = "#/main"; +export const selector = ".contact-top-bar"; +export function trigger(contact_topbar) { + const search_word = "furry"; + const images_apis = [ + "https://uapis.cn/api/imgapi/furry/img4k.php", + "https://uapis.cn/api/imgapi/furry/imgz4k.php", + "https://uapis.cn/api/imgapi/furry/imgs4k.php" + ]; + + const menu_item_template = document.createElement("template"); + menu_item_template.innerHTML = ` + +
+ + + +
+ 随机 Furry 图片 +
+ `; + + const menu_item = menu_item_template.content.firstElementChild; + const adder_button = contact_topbar.querySelector(".contact-adder-btn"); + const search_input = contact_topbar.querySelector("input"); + + menu_item.addEventListener("click", () => { + const random_image = images_apis[Math.floor(Math.random() * images_apis.length)]; + LiteLoader.api.openExternal(random_image); + }); + + adder_button.addEventListener("click", () => { + if (search_input.value.toLowerCase() == search_word) { + const context_menu = document.querySelector(".q-context-menu"); + context_menu.append(menu_item); + } + }); +} \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/easter_eggs/setting_navtab.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/easter_eggs/setting_navtab.js new file mode 100644 index 0000000..849ecf3 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/easter_eggs/setting_navtab.js @@ -0,0 +1,10 @@ +export const hash = "#/setting"; +export const selector = ".liteloader.nav-bar .nav-item[data-slug='config_view']"; +export function trigger(settingn_navtab_item) { + const new_name = "LiteLoaderNTQQ" + const random_number = Math.floor(Math.random() * 10000); + const name = settingn_navtab_item.querySelector(".name"); + if (random_number == 0) { + name.textContent = new_name; + } +} \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/init.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/init.js new file mode 100644 index 0000000..44c744a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/init.js @@ -0,0 +1,128 @@ +require("./liteloader_api/main.js"); + +const default_config = require("./setting/static/config.json"); + +const { protocol } = require("electron"); +const path = require("node:path"); +const fs = require("node:fs"); + + +protocol.registerSchemesAsPrivileged([ + { + scheme: "local", + privileges: { + standard: false, + allowServiceWorkers: true, + corsEnabled: false, + supportFetchAPI: true, + stream: true, + bypassCSP: true + } + } +]); + + +// 找到所有插件并挂载到 LiteLoader.plugins 对象 +(() => { + const output = (...args) => console.log("\x1b[32m%s\x1b[0m", "[LiteLoader]", ...args); + const config = LiteLoader.api.config.get("LiteLoader", default_config); + + // 如果设定环境变量 + if (process.env?.LITELOADERQQNT_PROFILE) { + output("Use LITELOADERQQNT_PROFILE environment variables:", LiteLoader.path.profile); + } + + // 如果插件全局开关为关闭状态 + if (!config.enable_plugins) { + output("Plugin loader is disabled, no plugins will be loaded."); + return; + } + + output("Start finding all plugins."); + + // 如果插件目录不存在 + if (!fs.existsSync(LiteLoader.path.plugins)) { + output("The plugins directory does not exist."); + output("A new plugin directory will be created."); + fs.mkdir(LiteLoader.path.plugins, { recursive: true }, error => { + if (error) { + output("Plugins directory creation failed!"); + output("Please check the plugins directory."); + return; + } + output("Plugins directory created successfully!"); + }); + return; + } + + // 读取插件目录 + try { + for (const plugin_pathname of fs.readdirSync(LiteLoader.path.plugins, "utf-8")) { + try { + const manifest_file = path.join(LiteLoader.path.plugins, plugin_pathname, "manifest.json"); + const manifest = JSON.parse(fs.readFileSync(manifest_file, "utf-8")); + + const incompatible_version = Number(manifest.manifest_version) != 4; + const incompatible_platform = !manifest.platform.includes(LiteLoader.os.platform); + const disabled_plugin = config.disabled_plugins.includes(manifest.slug); + + const plugin_path = path.join(LiteLoader.path.plugins, plugin_pathname); + const data_path = path.join(LiteLoader.path.data, manifest.slug); + + const main_file = path.join(plugin_path, manifest?.injects?.main ?? ""); + const preload_file = path.join(plugin_path, manifest?.injects?.preload ?? ""); + const renderer_file = path.join(plugin_path, manifest?.injects?.renderer ?? ""); + + LiteLoader.plugins[manifest.slug] = { + manifest: manifest, + incompatible: incompatible_version || incompatible_platform, + disabled: disabled_plugin, + path: { + plugin: plugin_path, + data: data_path, + injects: { + main: fs.statSync(main_file).isFile() ? main_file : null, + preload: fs.statSync(preload_file).isFile() ? preload_file : null, + renderer: fs.statSync(renderer_file).isFile() ? renderer_file : null + } + } + } + } + catch { + continue; + } + } + } + catch { + output("Failed to read plugins directory!"); + output("Please check the plugins directory."); + } + + // 输出插件状态 + for (const plugin of Object.values(LiteLoader.plugins)) { + const dependencies = plugin.manifest?.dependencies?.filter(slug => !LiteLoader.plugins[slug]); + // 输出不兼容 + if (plugin.incompatible) { + output("Found plugin:", plugin.manifest.name, "(Incompatible)"); + continue; + } + // 输出已禁用 + if (plugin.disabled) { + output("Found plugin:", plugin.manifest.name, "(Disabled)"); + continue; + } + // 缺少依赖 + if (dependencies?.length) { + output("Found plugin:", plugin.manifest.name, "Missing dependencies:", dependencies.toString()); + continue; + } + output("Found plugin:", plugin.manifest.name); + } + + // 输出找到了多少插件 + const plugins_length = Object.keys(LiteLoader.plugins).length; + output(`Found ${plugins_length} plugins,`, plugins_length ? "start loading plugins." : "no plugin to be loaded."); +})(); + + +require("./main.js"); diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/liteloader_api/main.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/liteloader_api/main.js new file mode 100644 index 0000000..79257fd --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/liteloader_api/main.js @@ -0,0 +1,110 @@ +const { ipcMain, shell } = require("electron"); +const path = require("node:path"); +const fs = require("node:fs"); + + +const root_path = path.join(__dirname, "..", ".."); +const profile_path = process.env.LITELOADERQQNT_PROFILE ?? root_path; +const data_path = path.join(profile_path, "data"); +const plugins_path = path.join(profile_path, "plugins"); +const liteloader_package = require(path.join(root_path, "package.json")); +const qqnt_package = require(path.join(process.resourcesPath, "app/package.json")) + + +function setConfig(slug, new_config) { + const config_path = path.join(data_path, slug, "config.json"); + fs.mkdirSync(path.dirname(config_path), { recursive: true }); + fs.writeFileSync(config_path, JSON.stringify(new_config, null, 4), "utf-8"); +} + + +function getConfig(slug, default_config) { + const config_path = path.join(data_path, slug, "config.json"); + if (fs.existsSync(config_path)) { + const config = JSON.parse(fs.readFileSync(config_path, "utf-8")); + return Object.assign({}, default_config, config); + } + else { + setConfig(slug, default_config); + return Object.assign({}, default_config, {}); + } +} + + +const LiteLoader = { + path: { + root: root_path, + profile: profile_path, + data: data_path, + plugins: plugins_path + }, + versions: { + qqnt: qqnt_package.version, + liteloader: liteloader_package.version, + node: process.versions.node, + chrome: process.versions.chrome, + electron: process.versions.electron + }, + os: { + platform: process.platform + }, + package: { + liteloader: liteloader_package, + qqnt: qqnt_package + }, + plugins: {}, + api: { + config: { + set: setConfig, + get: getConfig + }, + openExternal: shell.openExternal, + openPath: shell.openPath + } +}; + + +// 将LiteLoader对象挂载到全局 +Object.defineProperty(globalThis, "LiteLoader", { + configurable: false, + get() { + const stack = new Error().stack.split("\n")[2]; + if (stack.includes(LiteLoader.path.root)) { + return LiteLoader; + } + if (stack.includes(LiteLoader.path.profile)) { + return LiteLoader; + } + } +}); + + +// 将LiteLoader对象挂载到window +ipcMain.on("LiteLoader.LiteLoader.LiteLoader", (event) => { + event.returnValue = { + ...LiteLoader, + api: void null + } +}); + + +ipcMain.handle("LiteLoader.LiteLoader.api", (event, name, method, ...args) => { + if (name == "config") { + if (method == "get") { + return LiteLoader.api.config.get(...args); + } + if (method == "set") { + return LiteLoader.api.config.set(...args); + } + } + if (name == "openExternal") { + if (method == "openExternal") { + return LiteLoader.api.openExternal(...args); + } + } + if (name == "openPath") { + if (method == "openPath") { + return LiteLoader.api.openPath(...args); + } + } +}); diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/liteloader_api/preload.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/liteloader_api/preload.js new file mode 100644 index 0000000..8acf319 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/liteloader_api/preload.js @@ -0,0 +1,19 @@ +const { ipcRenderer, contextBridge } = require("electron"); + + +// LiteLoader +Object.defineProperty(globalThis, "LiteLoader", { + value: { + ...ipcRenderer.sendSync("LiteLoader.LiteLoader.LiteLoader"), + api: { + config: { + get: (...args) => ipcRenderer.invoke("LiteLoader.LiteLoader.api", "config", "get", ...args), + set: (...args) => ipcRenderer.invoke("LiteLoader.LiteLoader.api", "config", "set", ...args) + }, + openExternal: (...args) => ipcRenderer.invoke("LiteLoader.LiteLoader.api", "openExternal", "openExternal", ...args), + openPath: (...args) => ipcRenderer.invoke("LiteLoader.LiteLoader.api", "openPath", "openPath", ...args) + } + } +}); + +contextBridge.exposeInMainWorld("LiteLoader", LiteLoader); diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/main.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/main.js new file mode 100644 index 0000000..2a85ab6 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/main.js @@ -0,0 +1,131 @@ +const { Module } = require("module"); +const { net, ipcMain } = require("electron"); +const path = require("path"); +const fs = require("fs"); + + +const loader = (new class { + + #exports = {}; + + init() { + // 加载插件 + for (const [slug, plugin] of Object.entries(LiteLoader.plugins)) { + if (plugin.disabled || plugin.incompatible) { + continue; + } + if (plugin.path.injects.main) { + this.#exports[slug] = require(plugin.path.injects.main); + } + } + return this; + } + + onBrowserWindowCreated(window) { + for (const [slug, plugin] of Object.entries(this.#exports)) { + if (plugin?.onBrowserWindowCreated) { + plugin.onBrowserWindowCreated(window); + } + } + } + +}).init(); + + +ipcMain.handle("LiteLoader.LiteLoader.preload", (event) => { + const qqnt_preload_path = event.sender.preload; + return fs.readFileSync(qqnt_preload_path, "utf-8"); +}); + + +function processPreloadPath(qqnt_preload_path) { + const liteloader_preload_path = path.join(LiteLoader.path.root, "src/preload.js"); + const asar_path = qqnt_preload_path.split(".asar")[0]; + + let preload_path = ""; + + if (asar_path == qqnt_preload_path) { + const preload_path_dirname = path.dirname(qqnt_preload_path).replaceAll("\\", "/"); + preload_path = `${preload_path_dirname}/../application/preload.js`; + } + else { + const asar_path_dirname = path.dirname(qqnt_preload_path.split(".asar")[1]); + preload_path = `${asar_path}/../${path.basename(asar_path)}${asar_path_dirname}/preload.js`; + } + + if (!fs.existsSync(preload_path)) { + fs.mkdirSync(path.dirname(preload_path), { recursive: true }); + fs.copyFileSync(liteloader_preload_path, preload_path); + } + + if (fs.readFileSync(preload_path, "utf-8") != fs.readFileSync(liteloader_preload_path, "utf-8")) { + fs.copyFileSync(liteloader_preload_path, preload_path); + } + + return preload_path; +} + + +// 注册协议 +function protocolRegister(protocol) { + if (!protocol.isProtocolRegistered("local")) { + protocol.handle("local", (req) => { + const { host, pathname } = new URL(decodeURI(req.url)); + const filepath = path.normalize(pathname.slice(1)); + switch (host) { + case "root": return net.fetch(`file:///${LiteLoader.path.root}/${filepath}`); + case "profile": return net.fetch(`file:///${LiteLoader.path.profile}/${filepath}`); + default: return net.fetch(`file://${host}/${filepath}`); + } + }); + } +} + + +function proxyBrowserWindowConstruct(target, [config], newTarget) { + const qqnt_preload_path = config.webPreferences.preload; + const window = Reflect.construct(target, [ + { + ...config, + webPreferences: { + ...config.webPreferences, + webSecurity: false, + devTools: true, + preload: processPreloadPath(qqnt_preload_path), + additionalArguments: ["--fetch-schemes=local"] + } + } + ], newTarget); + + // 挂载窗口原preload + window.webContents.preload = qqnt_preload_path; + + //加载自定义协议 + protocolRegister(window.webContents.session.protocol); + + // 加载插件 + loader.onBrowserWindowCreated(window); + + return window; +} + + +// 监听窗口创建 +Module._load = new Proxy(Module._load, { + apply(target, thisArg, argArray) { + const module = Reflect.apply(target, thisArg, argArray); + if (argArray[0] == "electron") { + return new Proxy(module, { + get(target, property, receiver) { + if (property == "BrowserWindow") { + return new Proxy(module.BrowserWindow, { + construct: proxyBrowserWindowConstruct + }); + } + return Reflect.get(target, property, receiver); + } + }); + } + return module; + } +}); diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/preload.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/preload.js new file mode 100644 index 0000000..bbad1f5 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/preload.js @@ -0,0 +1,43 @@ +const { ipcRenderer } = require("electron"); + + +// 加载渲染进程 +document.addEventListener("readystatechange", () => { + if (document.readyState == "interactive") { + const script = document.createElement("script"); + script.type = "module"; + script.src = `local://root/src/renderer.js`; + document.head.prepend(script); + } +}); + + +const runPreloadScript = code => binding.createPreloadScript(` +(async function(require, process, Buffer, global, setImmediate, clearImmediate, exports, module) { + ${code} +}); +`)(...arguments); + + +// 加载插件 Preload +(async () => { + runPreloadScript(await (await fetch(`local://root/src/liteloader_api/preload.js`)).text()); + for (const [slug, plugin] of Object.entries(LiteLoader.plugins)) { + if (plugin.disabled || plugin.incompatible) { + continue; + } + if (plugin.path.injects.preload) { + runPreloadScript(await (await fetch(`local:///${plugin.path.injects.preload}`)).text()); + } + } +})(); + + +// 加载 QQNT Preload +let isLoaded = false; +ipcRenderer.invoke("LiteLoader.LiteLoader.preload").then(preload => { + if (!isLoaded) { + runPreloadScript(preload); + isLoaded = true; + } +}); diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/renderer.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/renderer.js new file mode 100644 index 0000000..2a6b4c4 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/renderer.js @@ -0,0 +1,82 @@ +import "./components/renderer.js"; +import "./easter_eggs/renderer.js"; +import { SettingInterface } from "./setting/renderer.js"; + + +const loader = await (new class { + + #exports = {}; + + async init() { + // 加载插件 + for (const [slug, plugin] of Object.entries(LiteLoader.plugins)) { + if (plugin.disabled || plugin.incompatible) { + continue; + } + if (plugin.path.injects.renderer) { + this.#exports[slug] = await import(`local:///${plugin.path.injects.renderer}`); + } + } + return this; + } + + async onSettingWindowCreated(settingInterface) { + for (const [slug, plugin] of Object.entries(this.#exports)) { + if (plugin?.onSettingWindowCreated) { + const view = await settingInterface.add(LiteLoader.plugins[slug]); + plugin.onSettingWindowCreated(view); + } + } + } + +}).init(); + + +// 寻找指定元素 +async function findElement(selector, callback) { + const observer = (_, observer) => { + const element = document.querySelector(selector); + if (element) { + callback(element); + observer?.disconnect?.(); + return true; + } + return false; + } + if (!observer()) { + new MutationObserver(observer).observe(document, { + subtree: true, + attributes: false, + childList: true + }); + } +} + + +// 监听页面变化 +async function watchURLHash(callback) { + if (!location.hash.includes("#/blank")) { + callback(location.hash); + } + else { + navigation.addEventListener("navigatesuccess", () => { + callback(location.hash) + }, { once: true }); + } +} + + +// 加载彩蛋 +async function loadSettingInterface(currentHash) { + if (currentHash.includes("#/setting")) { + const settingInterface = new SettingInterface(); + findElement(".setting-tab .nav-bar", async () => { + await settingInterface.SettingInit(); + await loader.onSettingWindowCreated(settingInterface); + }); + } +} + + +// 指定页面触发 +watchURLHash(loadSettingInterface); \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/renderer.js b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/renderer.js new file mode 100644 index 0000000..338feda --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/renderer.js @@ -0,0 +1,262 @@ +import default_config from "./static/config.json" assert {type: "json"}; + + +export class SettingInterface { + #liteloader_nav_bar = document.createElement("div"); + #liteloader_setting_view = document.createElement("div"); + #setting_view = document.querySelector(".setting-main .q-scroll-view"); + #setting_title = document.querySelector(".setting-main .setting-title"); + + constructor() { + this.#liteloader_nav_bar.classList.add("nav-bar", "liteloader"); + this.#liteloader_setting_view.classList.add("q-scroll-view", "scroll-view--show-scrollbar", "liteloader"); + document.querySelector(".setting-tab").append(this.#liteloader_nav_bar); + document.querySelector(".setting-main .setting-main__content").append(this.#liteloader_setting_view); + document.querySelector(".setting-tab").addEventListener("click", event => { + const nav_item = event.target.closest(".nav-item"); + if (nav_item) { + // 重新设定激活状态 + document.querySelectorAll(".setting-tab .nav-item").forEach(element => { + element.classList.remove("nav-item-active"); + }); + nav_item.classList.add("nav-item-active"); + // 内容显示 + if (nav_item.parentElement.classList.contains("liteloader")) { + this.#setting_view.style.display = "none"; + this.#liteloader_setting_view.style.display = "block"; + } + else { + this.#setting_view.style.display = "block"; + this.#liteloader_setting_view.style.display = "none"; + } + } + }); + } + + async add(plugin) { + const default_thumb = `local://root/src/setting/static/default.svg`; + const plugin_thumb = `local:///${plugin.path.plugin}/${plugin.manifest?.thumb}`; + const thumb = plugin.manifest.thumb ? plugin_thumb : default_thumb; + const nav_item = document.querySelector(".setting-tab .nav-item").cloneNode(true); + const view = document.createElement("div"); + nav_item.classList.remove("nav-item-active"); + nav_item.setAttribute("data-slug", plugin.manifest.slug); + nav_item.querySelector(".q-icon").innerHTML = await appropriateIcon(thumb); + nav_item.querySelector(".name").textContent = plugin.manifest.name; + nav_item.addEventListener("click", event => { + if (!event.currentTarget.classList.contains("nav-item-active")) { + this.#setting_title.childNodes[1].textContent = plugin.manifest.name; + this.#liteloader_setting_view.textContent = null; + this.#liteloader_setting_view.append(view); + } + }); + this.#liteloader_nav_bar.append(nav_item); + view.classList.add("tab-view", plugin.manifest.slug); + return view; + } + + async SettingInit() { + const style = document.createElement("link"); + style.rel = "stylesheet"; + style.type = "text/css"; + style.href = "local://root/src/setting/static/style.css"; + document.head.append(style); + const view = await this.add({ + manifest: { + slug: "config_view", + name: "LiteLoaderQQNT", + thumb: "./src/setting/static/default.svg" + }, + path: { + plugin: LiteLoader.path.root + } + }); + view.innerHTML = await (await fetch("local://root/src/setting/static/view.html")).text(); + initVersions(view); + initPluginList(view); + initPath(view); + initAbout(view); + } +} + + +// 禁用插件 +async function disablePlugin(slug, disabled) { + const config = await LiteLoader.api.config.get("LiteLoader", default_config); + if (disabled) { + config.disabled_plugins = config.disabled_plugins.concat(slug); + } + else { + config.disabled_plugins = config.disabled_plugins.filter(item => item != slug); + } + await LiteLoader.api.config.set("LiteLoader", config); +} + + +async function appropriateIcon(pluginIconUrlUsingLocalPotocol) { + if (pluginIconUrlUsingLocalPotocol.endsWith('.svg')) { + return await (await fetch(pluginIconUrlUsingLocalPotocol)).text(); + } else { + return ``; + } +} + + +async function initVersions(view) { + const liteloader = view.querySelectorAll(".versions .current .liteloader setting-text"); + const qqnt = view.querySelectorAll(".versions .current .qqnt setting-text"); + const electron = view.querySelectorAll(".versions .current .electron setting-text"); + const chromium = view.querySelectorAll(".versions .current .chromium setting-text"); + const nodejs = view.querySelectorAll(".versions .current .nodejs setting-text"); + + liteloader[1].textContent = LiteLoader.versions.liteloader; + qqnt[1].textContent = LiteLoader.versions.qqnt; + electron[1].textContent = LiteLoader.versions.electron; + chromium[1].textContent = LiteLoader.versions.chrome; + nodejs[1].textContent = LiteLoader.versions.node; + + const title = view.querySelector(".versions .new setting-text"); + const update_btn = view.querySelector(".versions .new setting-button"); + + const jump_link = () => LiteLoader.api.openExternal(update_btn.value); + const try_again = () => { + // 初始化 显示 + title.textContent = "正在瞅一眼 LiteLoaderQQNT 是否有新版本"; + update_btn.textContent = "你先别急"; + update_btn.value = null; + update_btn.removeEventListener("click", jump_link); + update_btn.removeEventListener("click", try_again); + // 检测是否有新版 + const repo_url = LiteLoader.package.liteloader.repository.url; + const release_latest_url = `${repo_url.slice(0, repo_url.lastIndexOf(".git"))}/releases/latest`; + fetch(release_latest_url).then((res) => { + const new_version = res.url.slice(res.url.lastIndexOf("/") + 1); + // 有新版 + if (LiteLoader.versions.liteloader != new_version) { + title.textContent = `发现 LiteLoaderQQNT 新版本 ${new_version}`; + update_btn.textContent = "去瞅一眼"; + update_btn.value = res.url; + update_btn.removeEventListener("click", try_again); + update_btn.addEventListener("click", jump_link); + } + // 没新版 + else { + title.textContent = "暂未发现 LiteLoaderQQNT 有新版本,目前已是最新"; + update_btn.textContent = "重新发现"; + update_btn.value = null; + update_btn.removeEventListener("click", jump_link); + update_btn.addEventListener("click", try_again); + } + }).catch((e) => { + title.textContent = `检查更新时遇到错误:${e}`; + update_btn.textContent = "重新发现"; + update_btn.value = null; + update_btn.removeEventListener("click", jump_link); + update_btn.addEventListener("click", try_again); + }); + }; + + try_again(); +} + + +async function initPluginList(view) { + const plugin_item_template = view.querySelector("#plugin-item"); + const plugin_loader_switch = view.querySelector(".plugins .loader setting-switch"); + const plugin_lists = { + extension: view.querySelector(".plugins .extension"), + theme: view.querySelector(".plugins .theme"), + framework: view.querySelector(".plugins .framework"), + }; + + const config = await LiteLoader.api.config.get("LiteLoader", default_config); + plugin_loader_switch.toggleAttribute("is-active", config.enable_plugins); + plugin_loader_switch.addEventListener("click", () => { + const isActive = plugin_loader_switch.hasAttribute("is-active"); + plugin_loader_switch.toggleAttribute("is-active", !isActive); + config.enable_plugins = !isActive; + LiteLoader.api.config.set("LiteLoader", config); + }); + + for (const [slug, plugin] of Object.entries(LiteLoader.plugins)) { + // 跳过不兼容插件 + if (plugin.incompatible) { + continue; + } + + const default_icon = `local://root/src/setting/static/default.png`; + const plugin_icon = `local:///${plugin.path.plugin}/${plugin.manifest?.icon}`; + const icon = plugin.manifest?.icon ? plugin_icon : default_icon; + + const plugin_list = plugin_lists[plugin.manifest.type] || plugin_lists.extension; + const plugin_item = plugin_item_template.content.cloneNode(true); + + const plugin_item_icon = plugin_item.querySelector(".icon"); + const plugin_item_name = plugin_item.querySelector(".name"); + const plugin_item_description = plugin_item.querySelector(".description"); + const plugin_item_version = plugin_item.querySelector(".version"); + const plugin_item_authors = plugin_item.querySelector(".authors"); + const plugin_item_switch = plugin_item.querySelector(".switch"); + + plugin_item_icon.innerHTML = await appropriateIcon(icon); + plugin_item_name.textContent = plugin.manifest.name; + plugin_item_name.title = plugin.manifest.name; + plugin_item_description.textContent = plugin.manifest.description; + plugin_item_description.title = plugin.manifest.description; + plugin_item_version.textContent = plugin.manifest.version; + + plugin.manifest.authors.forEach((author, index, array) => { + const author_link = document.createElement("a"); + author_link.textContent = author.name; + author_link.addEventListener("click", () => LiteLoader.api.openExternal(author.link)); + plugin_item_authors.append(author_link); + if (index < array.length - 1) { + plugin_item_authors.append(" | "); + } + }); + + plugin_item_switch.toggleAttribute("is-active", !LiteLoader.plugins[slug].disabled); + plugin_item_switch.addEventListener("click", () => { + const isActive = plugin_item_switch.hasAttribute("is-active"); + plugin_item_switch.toggleAttribute("is-active", !isActive); + disablePlugin(slug, isActive); + }); + + plugin_list.append(plugin_item); + } +} + + +async function initPath(view) { + const root_path_content = view.querySelectorAll(".path .root setting-text")[2]; + const root_path_button = view.querySelector(".path .root setting-button"); + const profile_path_content = view.querySelectorAll(".path .profile setting-text")[2]; + const profile_path_button = view.querySelector(".path .profile setting-button"); + + root_path_content.textContent = LiteLoader.path.root; + root_path_button.addEventListener("click", () => LiteLoader.api.openPath(LiteLoader.path.root)); + profile_path_content.textContent = LiteLoader.path.profile; + profile_path_button.addEventListener("click", () => LiteLoader.api.openPath(LiteLoader.path.profile)); +} + + +async function initAbout(view) { + const liteloaderqqnt = view.querySelector(".about .liteloaderqqnt"); + const github = view.querySelector(".about .github"); + const group = view.querySelector(".about .group"); + const channel = view.querySelector(".about .channel"); + + liteloaderqqnt.addEventListener("click", () => LiteLoader.api.openExternal("https://liteloaderqqnt.github.io")); + github.addEventListener("click", () => LiteLoader.api.openExternal("https://github.com/LiteLoaderQQNT")); + group.addEventListener("click", () => LiteLoader.api.openExternal("https://t.me/LiteLoaderQQNT")); + channel.addEventListener("click", () => LiteLoader.api.openExternal("https://t.me/LiteLoaderQQNT_Channel")); + + // Hitokoto - 一言 + const fetchHitokoto = async () => { + const { hitokoto, creator } = await (await fetch("https://v1.hitokoto.cn")).json(); + view.querySelector(".about .hitokoto_text").textContent = hitokoto; + view.querySelector(".about .hitokoto_author").textContent = creator; + }; + fetchHitokoto(); + setInterval(fetchHitokoto, 1000 * 10); +} \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/config.json b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/config.json new file mode 100644 index 0000000..c05c993 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/config.json @@ -0,0 +1,4 @@ +{ + "enable_plugins": true, + "disabled_plugins": [] +} \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/default.png b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/default.png new file mode 100644 index 0000000..914df49 Binary files /dev/null and b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/default.png differ diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/default.svg b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/default.svg new file mode 100644 index 0000000..3ea13b7 --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/default.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/style.css b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/style.css new file mode 100644 index 0000000..c47368a --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/style.css @@ -0,0 +1,98 @@ +/* 侧边栏 */ +.setting-tab { + & { + display: flex; + flex-direction: column; + } + + & .nav-bar { + & { + flex-shrink: 0; + } + + &.liteloader { + & { + flex: 1; + margin-top: 25px; + overflow-x: hidden; + overflow-y: scroll; + } + + &::before { + content: ""; + display: block; + position: absolute; + transform: translate(50px, -15px); + width: 100px; + height: 5px; + border-radius: 5px; + background: rgba(127, 127, 127, 0.5); + } + } + } +} + + +/* 主内容 */ +.setting-main .q-scroll-view.liteloader .tab-view { + font-size: 14px; + padding: 20px 20px 0px; +} + + + +/* 设置页面样式 */ +.config_view .plugins :is(.extension, .theme, .framework) setting-item { + & { + gap: 8px; + } + + & > div { + & { + flex: 1; + display: flex; + flex-direction: column; + gap: 8px; + } + + & > div { + height: 48px; + display: flex; + align-items: center; + gap: 8px; + } + + & > setting-text { + & { + display: flex; + gap: 8px; + } + + & :nth-child(1) { + width: 120px; + } + } + } + + /* 插件缩略图 */ + & .icon :is(img, svg) { + width: 48px; + height: 48px; + border-radius: 8px; + } + + /* 插件附加信息 */ + & a { + & { + color: var(--text-link); + } + + &:hover { + color: var(--hover-link); + } + + &:active { + color: var(--press-link); + } + } +} \ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/view.html b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/view.html new file mode 100644 index 0000000..7a564ad --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/LiteLoaderQQNT/src/setting/static/view.html @@ -0,0 +1,174 @@ + + + + + + + + LiteLoader + + + + QQNT + + + + Electron + + + + Chromium + + + + Node.js + + + + + + + + 正在发现 LiteLoaderQQNT 是否有新版本 + 你先别急 + + + + + + + + + + + 启用插件加载器 + + + + + + + + + + + + + + + + + + + + + + + +
+ 本体目录位置 + LiteLoaderQQNT 本体的安装位置,可以安装到任意位置 + +
+ 进入目录 +
+ +
+ 数据目录位置 + 可通过 LITELOADERQQNT_PROFILE 环境变量指定数据存放位置 + +
+ 进入目录 +
+
+
+
+ + + + + + +
+ LiteLoaderQQNT + LiteLoaderQQNT 是 QQNT 的插件加载器,一般在 QQNT 的环境内简称为LiteLoader + 它可以让你自由地为 QQNT 添加各种插件,并实现例如美化主题、增加功能等各种功能 +
+
+ +
+ 开源协议 + MIT License +
+
+
+
+ + + +
+ LiteLoaderQQNT 官网 + https://liteloaderqqnt.github.io +
+ 进去瞅瞅 +
+ +
+ GitHub 仓库 + https://github.com/LiteLoaderQQNT +
+ 进去瞅瞅 +
+ +
+ Telegram 群组 + https://t.me/LiteLoaderQQNT +
+ 进去瞅瞅 +
+ +
+ Telegram 频道 + https://t.me/LiteLoaderQQNT_Channel +
+ 进去瞅瞅 +
+
+
+ + + + 正在加载:Hitokoto - 一言 +
+
+ +
+
+ —— + LiteLoaderQQNT +
+
+
+
+
\ No newline at end of file diff --git a/RevokeMsgPatcher.v1.8.1/RevokeMsgPatcher.MultiInstance.exe b/RevokeMsgPatcher.v1.8.1/RevokeMsgPatcher.MultiInstance.exe new file mode 100644 index 0000000..96b5f82 Binary files /dev/null and b/RevokeMsgPatcher.v1.8.1/RevokeMsgPatcher.MultiInstance.exe differ diff --git a/RevokeMsgPatcher.v1.8.1/RevokeMsgPatcher.exe b/RevokeMsgPatcher.v1.8.1/RevokeMsgPatcher.exe new file mode 100644 index 0000000..83485ea Binary files /dev/null and b/RevokeMsgPatcher.v1.8.1/RevokeMsgPatcher.exe differ diff --git a/RevokeMsgPatcher.v1.8.1/防撤回使用说明.txt b/RevokeMsgPatcher.v1.8.1/防撤回使用说明.txt new file mode 100644 index 0000000..8cb52db --- /dev/null +++ b/RevokeMsgPatcher.v1.8.1/防撤回使用说明.txt @@ -0,0 +1,11 @@ +ǰĿ¼Ӧļ + +- RevokeMsgPatcher.exe PC΢/QQ/TIMز +- RevokeMsgPatcher.MultiInstance.exe һͨõ΢Ŷ࿪ +- LiteLoaderQQNT QQNThttps://github.com/LiteLoaderQQNT/LiteLoaderQQNT + +PC΢/QQ/TIMزʹ˵ +https://github.com/huiyadanli/RevokeMsgPatcher + +һͨõ΢Ŷ࿪ʹ˵ +https://github.com/huiyadanli/RevokeMsgPatcher/tree/master/RevokeMsgPatcher.MultiInstance \ No newline at end of file