Skip to content

Commit

Permalink
Feat: protobuf support (LNReader#1003)
Browse files Browse the repository at this point in the history
* feat: protobuf support

* fix: spead operator is slower

* fix: redundant JSON encoding

* add fetchProto to plugin sandbox
  • Loading branch information
nyagami authored Mar 21, 2024
1 parent 817e4dc commit 8906253
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 2 deletions.
83 changes: 83 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"expo-web-browser": "~12.3.2",
"i18n-js": "^3.8.0",
"lodash-es": "^4.17.21",
"protobufjs": "^7.2.6",
"qs": "^6.11.2",
"react": "18.2.0",
"react-native": "0.72.10",
Expand Down
93 changes: 93 additions & 0 deletions src/plugins/helpers/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getUserAgent } from '@hooks/persisted/useUserAgent';
import { parse as parseProto } from 'protobufjs';

type FetchInit = {
headers?: Record<string, string> | Headers;
Expand Down Expand Up @@ -111,3 +112,95 @@ export const fetchText = async (
return '';
}
};

function base64ToBytesArr(str: string) {
const abc = [
...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
]; // base64 alphabet
let result = [];

for (let i = 0; i < str.length / 4; i++) {
let chunk = [...str.slice(4 * i, 4 * i + 4)];
let bin = chunk
.map(x => abc.indexOf(x).toString(2).padStart(6, '0'))
.join('');
let bytes = bin.match(/.{1,8}/g)?.map(x => +('0b' + x)) || [];
result.push(
...bytes.slice(
0,
3 - Number(str[4 * i + 2] === '=') - Number(str[4 * i + 3] === '='),
),
);
}
return result;
}

interface ProtoRequestInit {
// merged .proto file
proto: string;
requestType: string;
requestData?: any;
responseType: string;
}

const BYTE_MARK = BigInt((1 << 8) - 1);

export const fetchProto = async function (
protoInit: ProtoRequestInit,
url: string,
init?: FetchInit,
) {
const protoRoot = parseProto(protoInit.proto).root;
const RequestMessge = protoRoot.lookupType(protoInit.requestType);
if (RequestMessge.verify(protoInit.requestData)) {
throw new Error('Invalid Proto');
}
// encode request data
const encodedrequest = RequestMessge.encode(protoInit.requestData).finish();
const requestLength = BigInt(encodedrequest.length);
const headers = new Uint8Array(
Array(5)
.fill(0)
.map((v, idx) => {
if (idx === 0) {
return 0;
}
return Number((requestLength >> BigInt(8 * (5 - idx - 1))) & BYTE_MARK);
}),
);
init = await makeInit(init);
const bodyArray = new Uint8Array(headers.length + encodedrequest.length);
bodyArray.set(headers, 0);
bodyArray.set(encodedrequest, headers.length);

return fetch(url, {
method: 'POST',
...init,
body: bodyArray,
} as RequestInit)
.then(r => r.blob())
.then(blob => {
// decode response data
return new Promise((resolve, reject) => {
const fr = new FileReader();
fr.onloadend = () => {
const payload = new Uint8Array(
base64ToBytesArr(
fr.result.slice(FILE_READER_PREFIX_LENGTH) as string,
),
);
const length = Number(
BigInt(payload[1] << 24) |
BigInt(payload[2] << 16) |
BigInt(payload[3] << 8) |
BigInt(payload[4]),
);
const ResponseMessage = protoRoot.lookupType(protoInit.responseType);
resolve(ResponseMessage.decode(payload.slice(5, 5 + length)));
};
fr.onerror = () => reject();
fr.onabort = () => reject();
fr.readAsDataURL(blob);
});
});
};
4 changes: 2 additions & 2 deletions src/plugins/pluginManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import qs from 'qs';
import { NovelStatus, Plugin, PluginItem } from './types';
import { FilterTypes } from './types/filterTypes';
import { isUrlAbsolute } from './helpers/isAbsoluteUrl';
import { fetchApi, fetchFile, fetchText } from './helpers/fetch';
import { fetchApi, fetchFile, fetchProto, fetchText } from './helpers/fetch';
import { defaultCover } from './helpers/constants';
import { encode, decode } from 'urlencode';
import TextFile from '@native/TextFile';
Expand All @@ -23,7 +23,7 @@ const packages: Record<string, any> = {
'qs': qs,
'urlencode': { encode, decode },
'@libs/novelStatus': { NovelStatus },
'@libs/fetch': { fetchApi, fetchFile, fetchText },
'@libs/fetch': { fetchApi, fetchFile, fetchText, fetchProto },
'@libs/isAbsoluteUrl': { isUrlAbsolute },
'@libs/filterInputs': { FilterTypes },
'@libs/defaultCover': { defaultCover },
Expand Down

0 comments on commit 8906253

Please sign in to comment.