Initial let parser use bytebuffer

This commit is contained in:
tarsin committed 2023-07-28 17:24:42 +08:00
1 parent cf8097babc
commit 02855b68e6
5 files changed
+66 -16

No files matched your search

@@ -45,10 +45,14 @@ import com.hippo.ehviewer.client.parser.TorrentParser
import com.hippo.ehviewer.client.parser.TorrentResult
import com.hippo.ehviewer.client.parser.VoteCommentResult
import com.hippo.ehviewer.client.parser.VoteTagParser
import com.hippo.ehviewer.cronet.awaitBodyFully
import com.hippo.ehviewer.cronet.cronetRequest
import com.hippo.ehviewer.cronet.execute
import com.hippo.ehviewer.dailycheck.showEventNotification
import com.hippo.ehviewer.dailycheck.today
import com.hippo.ehviewer.util.AppConfig
import com.hippo.ehviewer.util.StatusCodeException
import io.ktor.utils.io.pool.DirectByteBufferPool
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.delay
import kotlinx.serialization.json.add
@@ -65,6 +69,7 @@ import okhttp3.RequestBody.Companion.asRequestBody
import org.jsoup.Jsoup
import splitties.init.appCtx
import java.io.File
import java.nio.ByteBuffer
import kotlin.math.ceil
private const val TAG = "EhEngine"
@@ -124,6 +129,25 @@ private fun rethrowExactly(code: Int, headers: Headers, body: String, e: Throwab
throw e
}
val httpContentPool = DirectByteBufferPool(16, 16384)
suspend inline fun <T> fetchCompat(url: String, referer: String? = null, crossinline parser: (ByteBuffer) -> T): T {
val ret: T
cronetRequest(url, referer).execute {
val buffer = httpContentPool.borrow()
try {
awaitBodyFully {
buffer.put(it)
}
buffer.flip()
ret = parser(buffer)
} finally {
httpContentPool.recycle(buffer)
}
}
return ret
}
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
private suspend inline fun <T> Request.executeAndParsingWith(block: suspend String.() -> T): T {
Log.d(TAG, url.toString())
@@ -151,7 +175,7 @@ object EhEngine {
suspend fun getTorrentList(url: String, gid: Long, token: String?): TorrentResult {
val referer = EhUrl.getGalleryDetailUrl(gid, token)
return ehRequest(url, referer).executeAndParsingWith(TorrentParser::parse)
return fetchCompat(url, referer, TorrentParser::parse)
}
suspend fun getArchiveList(url: String, gid: Long, token: String?) = ehRequest(url, EhUrl.getGalleryDetailUrl(gid, token))
@@ -1,7 +1,9 @@
package com.hippo.ehviewer.client.parser
import java.nio.ByteBuffer
object TorrentParser {
fun parse(body: String) = parseTorrent(body)
fun parse(body: ByteBuffer) = parseTorrent(body)
}
class Torrent(
@@ -18,4 +20,4 @@ fun Torrent.format() = "[$posted] $name [$size] [↑$seeds ↓$peers ✓$downloa
typealias TorrentResult = ArrayList<Torrent>
private external fun parseTorrent(body: String): ArrayList<Torrent>
private external fun parseTorrent(body: ByteBuffer, size: Int = body.limit()): ArrayList<Torrent>
@@ -19,6 +19,8 @@ import org.chromium.net.UrlResponseInfo
import splitties.init.appCtx
import java.nio.ByteBuffer
import java.nio.channels.FileChannel
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
@@ -72,7 +74,7 @@ class CronetRequest {
}
}
inline fun cronetRequest(url: String, referer: String? = null, conf: UrlRequest.Builder.() -> Unit) = CronetRequest().apply {
inline fun cronetRequest(url: String, referer: String? = null, conf: UrlRequest.Builder.() -> Unit = {}) = CronetRequest().apply {
request = cronetHttpClient.newUrlRequestBuilder(url, callback, cronetHttpClientExecutor).apply {
addHeader("Cookie", EhCookieStore.getCookieHeader(url.toHttpUrl()))
addHeader("User-Agent", CHROME_USER_AGENT)
@@ -104,11 +106,16 @@ suspend inline fun CronetRequest.copyToChannel(chan: FileChannel, crossinline li
listener(bytes)
}
suspend inline fun CronetRequest.execute(crossinline callback: suspend CronetRequest.(UrlResponseInfo) -> Unit) = coroutineScope {
onResponse = { launch { callback(it) } }
suspendCancellableCoroutine { cont ->
cont.invokeOnCancellation { request.cancel() }
daemonCont = cont
request.start()
suspend inline fun CronetRequest.execute(crossinline callback: suspend CronetRequest.(UrlResponseInfo) -> Unit): Boolean {
contract {
callsInPlace(callback, InvocationKind.EXACTLY_ONCE)
}
return coroutineScope {
onResponse = { launch { callback(it) } }
suspendCancellableCoroutine { cont ->
cont.invokeOnCancellation { request.cancel() }
daemonCont = cont
request.start()
}
}
}
+17 -1
View File
@@ -13,11 +13,13 @@ extern crate regex_lite;
extern crate tl;
use android_logger::Config;
use jnix::jni::objects::JString;
use jnix::jni::objects::{JByteBuffer, JString};
use jnix::jni::sys::{jint, JavaVM, JNI_VERSION_1_6};
use jnix::JnixEnv;
use log::LevelFilter;
use std::ffi::c_void;
use std::ptr::slice_from_raw_parts;
use std::str::from_utf8;
use tl::{Bytes, Node, NodeHandle, Parser, VDom};
#[macro_export]
@@ -70,6 +72,20 @@ where
f(&dom, parser, env)
}
fn parse_bytebuffer<F, R>(env: &mut JnixEnv, str: JByteBuffer, limit: jint, mut f: F) -> Option<R>
where
F: FnMut(&VDom, &Parser, &JnixEnv) -> Option<R>,
{
let ptr = env.get_direct_buffer_address(str).ok()?;
let html = unsafe {
let buff = slice_from_raw_parts(ptr, limit as usize);
from_utf8(&*buff).ok()?
};
let dom = tl::parse(html, tl::ParserOptions::default()).ok()?;
let parser = dom.parser();
f(&dom, parser, env)
}
fn get_node_handle_attr<'a>(
node: &NodeHandle,
parser: &'a Parser,
+6 -5
View File
@@ -1,12 +1,13 @@
use catch_panic::catch_panic;
use jni_fn::jni_fn;
use jnix::jni::objects::{JClass, JString};
use jnix::jni::sys::jobject;
use jnix::jni::objects::{JByteBuffer, JClass};
use jnix::jni::sys::{jint, jobject};
use jnix::jni::JNIEnv;
use jnix::{IntoJava, JnixEnv};
use jnix_macros::IntoJava;
use parse_bytebuffer;
use quick_xml::escape::unescape;
use {parse_jni_string, regex};
use regex;
#[derive(Default, IntoJava)]
#[jnix(package = "com.hippo.ehviewer.client.parser")]
@@ -24,9 +25,9 @@ pub struct Torrent {
#[catch_panic(default = "std::ptr::null_mut()")]
#[allow(non_snake_case)]
#[jni_fn("com.hippo.ehviewer.client.parser.TorrentParserKt")]
pub fn parseTorrent(env: JNIEnv, _class: JClass, input: JString) -> jobject {
pub fn parseTorrent(env: JNIEnv, _class: JClass, buffer: JByteBuffer, limit: jint) -> jobject {
let mut env = JnixEnv { env };
parse_jni_string(&mut env, &input, |dom, parser, _env| {
parse_bytebuffer(&mut env, buffer, limit, |dom, parser, _env| {
Some(dom.query_selector("table")?.filter_map(|e| {
let html = e.get(parser)?.inner_html(parser);
let grp = regex!("</span> ([0-9-]+) [0-9:]+</td>[\\s\\S]+</span> ([0-9.]+ [KMGT]B)</td>[\\s\\S]+</span> ([0-9]+)</td>[\\s\\S]+</span> ([0-9]+)</td>[\\s\\S]+</span> ([0-9]+)</td>[\\s\\S]+</span>([^<]+)</td>[\\s\\S]+onclick=\"document.location='([^\"]+)'[^<]+>([^<]+)</a>").captures(&html)?;