From ce8351a060ffbb41b2bcc505d04992b388e42880 Mon Sep 17 00:00:00 2001 From: fumiama Date: Wed, 19 May 2021 21:54:46 +0800 Subject: [PATCH] =?UTF-8?q?v2.1=201.=20=E9=80=82=E9=85=8DSPB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/compiler.xml | 2 +- .idea/dictionaries/rumia.xml | 1 + .idea/misc.xml | 2 +- .idea/runConfigurations.xml | 10 +++ app/build.gradle | 4 +- .../java/top/fumiama/simpledict/Client.kt | 76 ++++++++++++----- .../java/top/fumiama/simpledict/SimpleDict.kt | 81 +++++++++--------- .../fumiama/simpledict/SimpleProtobuf.java | 84 +++++++++++++++++++ build.gradle | 2 +- 9 files changed, 193 insertions(+), 69 deletions(-) create mode 100644 .idea/runConfigurations.xml create mode 100644 app/src/main/java/top/fumiama/simpledict/SimpleProtobuf.java diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 61a9130..fb7f4a8 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/dictionaries/rumia.xml b/.idea/dictionaries/rumia.xml index 1a0979a..258e643 100644 --- a/.idea/dictionaries/rumia.xml +++ b/.idea/dictionaries/rumia.xml @@ -1,6 +1,7 @@ + slle spwd diff --git a/.idea/misc.xml b/.idea/misc.xml index d5d35ec..860da66 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..797acea --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 39369d2..c1953a0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { applicationId "top.fumiama.simpledict" minSdkVersion 26 targetSdkVersion 30 - versionCode 15 - versionName '2.0.1' + versionCode 16 + versionName '2.1' resConfigs "zh", "zh-rCN" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/top/fumiama/simpledict/Client.kt b/app/src/main/java/top/fumiama/simpledict/Client.kt index 94540b9..cef6bc1 100644 --- a/app/src/main/java/top/fumiama/simpledict/Client.kt +++ b/app/src/main/java/top/fumiama/simpledict/Client.kt @@ -62,30 +62,58 @@ class Client(private val ip: String, private val port: Int) { return false } - fun receiveRawMessage(totalSize: Int = -1, bufferSize: Int = 1048576) : ByteArray { - var re = byteArrayOf() - try { - if (isConnect) { - Log.d("MyC", "开始接收服务端信息") - val inMessage = ByteArray(bufferSize) //设置接受缓冲,避免接受数据过长占用过多内存 - var a: Int - do { - a = din?.read(inMessage)?:0 //a存储返回消息的长度 - if(a > 0) { - re += inMessage.copyOf(a) - Log.d("MyC", "reply length:$a") - if(totalSize < 0 && a < bufferSize) break - } else break - } while (totalSize > re.size) - } else Log.d("MyC", "no connect to receive message") - } catch (e: IOException) { - Log.d("MyC", "receive message failed") - e.printStackTrace() + var buffer = byteArrayOf() + + fun receiveRawMessage(totalSize: Int = -1, bufferSize: Int = 1048576, setProgress: Boolean = false) : ByteArray { + if(totalSize == buffer.size) { + val re = buffer + buffer = byteArrayOf() + return re + } else { + var re = byteArrayOf() + try { + if (isConnect) { + Log.d("MyC", "开始接收服务端信息") + val inMessage = ByteArray(bufferSize) //设置接受缓冲,避免接受数据过长占用过多内存 + var a: Int + do { + a = din?.read(inMessage)?:0 //a存储返回消息的长度 + if(a > 0) { + re += inMessage.copyOf(a) + Log.d("MyC", "reply length:$a") + if(totalSize < 0 && a < bufferSize) break + else if(setProgress && totalSize > 0) progress?.notify(100 * re.size / totalSize) + } else break + } while (totalSize > re.size) + } else Log.d("MyC", "no connect to receive message") + } catch (e: IOException) { + Log.d("MyC", "receive message failed") + e.printStackTrace() + } + if(totalSize > 0 && re.size > totalSize) { + Log.d("MyC", "Reduce re size from ${re.size} to $totalSize") + buffer += re.copyOfRange(totalSize, re.size) + re = re.copyOf(totalSize) + } else if(totalSize > 0 && buffer.isNotEmpty()) { + Log.d("MyC", "Increase re size.") + buffer += re + if(buffer.size > totalSize) { + re = buffer.copyOf(totalSize) + buffer = buffer.copyOfRange(totalSize, buffer.size) + } else { + re = buffer + buffer = byteArrayOf() + } + } else if(totalSize < 0 && buffer.isNotEmpty()) { + re = buffer + buffer = byteArrayOf() + Log.d("MyC", "clear buffer") + } + return re } - return re } - fun receiveMessage() = receiveRawMessage().decodeToString() + fun receiveMessage(totalSize: Int = -1) = receiveRawMessage(totalSize).decodeToString() /** * 关闭连接 @@ -102,4 +130,10 @@ class Client(private val ip: String, private val port: Int) { e.printStackTrace() false } + + var progress: Progress? = null + + interface Progress { + fun notify(progressPercentage: Int) + } } diff --git a/app/src/main/java/top/fumiama/simpledict/SimpleDict.kt b/app/src/main/java/top/fumiama/simpledict/SimpleDict.kt index 8734949..c3cac65 100644 --- a/app/src/main/java/top/fumiama/simpledict/SimpleDict.kt +++ b/app/src/main/java/top/fumiama/simpledict/SimpleDict.kt @@ -1,7 +1,6 @@ package top.fumiama.simpledict import android.util.Log -import java.lang.Thread.sleep class SimpleDict(private val client: Client, private val pwd: String, private val spwd: String?) { //must run in thread private var dict = HashMap() @@ -16,39 +15,38 @@ class SimpleDict(private val client: Client, private val pwd: String, private va do { re = byteArrayOf() if(initDict()) { - sendMessageWithDelay("cat", 2333) + sendMessage("cat") try { firstRecv = client.receiveRawMessage() val firstStr = firstRecv.decodeToString() var length = "" + Log.d("MySD", "first str: $firstStr") for ((i, c) in firstStr.withIndex()) { if(c.isDigit()) length += c else { - if(i + 1 < firstRecv.size) re = firstRecv.copyOfRange(i, firstRecv.size) + if(i + 2 < firstRecv.size) re = firstRecv.copyOfRange(i + 1, firstRecv.size) break } } + Log.d("MySD", "length: $length") re += client.receiveRawMessage(length.toInt() - re.size) + closeDict() break } catch (e: Exception){ e.printStackTrace() + closeDict() } - closeDict() } } while (times-- > 0) return if(re.isEmpty()) null else re } - private fun sendMessageWithDelay(msg: CharSequence, delay: Long = 233) = Thread{ - client.sendMessage(msg) - sleep(delay) - }.start() + private fun sendMessage(msg: CharSequence) = client.sendMessage(msg) private fun initDict(): Boolean { if(client.initConnect()){ if(client.sendMessage(pwd)) { - client.receiveRawMessage() - sleep(233) + client.receiveRawMessage(31) return true } } @@ -62,19 +60,6 @@ class SimpleDict(private val client: Client, private val pwd: String, private va return false } - private fun analyzeDictBlk(dictBlock: ByteArray) { - Log.d("MySD", "Read block: ${dictBlock.decodeToString()}") - val keyLen = dictBlock[63].toInt().let { if (it > 63) 63 else it } - val dataEnd = 64 + dictBlock[127].toInt().let { if (it > 63) 63 else it } - val key = dictBlock.copyOf(keyLen).decodeToString() - val data = if (dataEnd > 64) dictBlock.copyOfRange(64, dataEnd).decodeToString() else null - if(key != "") { - dict[key] = data - latestKeys += key - } - Log.d("MySD", "Fetch $key=$data") - } - fun filterValues(predicate: (String?) -> Boolean) = dict.filterValues(predicate) fun fetchDict(doOnLoadFailure: ()->Unit = { @@ -82,11 +67,16 @@ class SimpleDict(private val client: Client, private val pwd: String, private va }, doOnLoadSuccess: ()->Unit = { Log.d("MySD", "Fetch dict success") }, doCommon: (() -> Unit)? = null) { - val dictBlock = ByteArray(128) dict = hashMapOf() latestKeys = arrayOf() - raw?.inputStream()?.let { - while (it.read(dictBlock, 0, 128) == 128) analyzeDictBlk(dictBlock) + raw?.let { + SimpleProtobuf.getDictArray(it).forEach { d -> + d?.apply { + val k = key.decodeToString() + dict[k] = data.decodeToString() + latestKeys += k + } + } doOnLoadSuccess() }?:doOnLoadFailure() doCommon?.let { it() } @@ -95,10 +85,11 @@ class SimpleDict(private val client: Client, private val pwd: String, private va fun del(key: String): Boolean { if(spwd == null) return false else if(initDict()) { - sendMessageWithDelay("del$spwd") - client.receiveMessage() - sendMessageWithDelay(key) - if(client.receiveMessage() == "succ") { + val delPass = "del$spwd" + sendMessage(delPass) + client.receiveRawMessage(delPass.length) + sendMessage(key) + if(client.receiveMessage(4) == "succ") { if(closeDict()) { dict.remove(key) val end = latestKeys.size-1 @@ -119,18 +110,22 @@ class SimpleDict(private val client: Client, private val pwd: String, private va operator fun get(key: String) = dict[key] fun set(key: String, value: String): Boolean { - if(spwd == null) return false - else if(initDict()) { - sendMessageWithDelay("set$spwd") - client.receiveMessage() - sendMessageWithDelay(key) - if(client.receiveMessage() == "data") { - sendMessageWithDelay(value) - client.receiveMessage() - if(closeDict()) dict[key] = value - return true - } else closeDict() - } - return false + //if(spwd == null) return false + val contain = dict.containsKey(key) + if((contain && del(key)) || !contain) { + if(initDict()) { + val setPass = "set$spwd" + sendMessage(setPass) + client.receiveRawMessage(setPass.length) + sendMessage(key) + if(client.receiveMessage(4) == "data") { + sendMessage(value) + client.receiveMessage(4) + if(closeDict()) dict[key] = value + return true + } else closeDict() + } + return false + } else return false } } \ No newline at end of file diff --git a/app/src/main/java/top/fumiama/simpledict/SimpleProtobuf.java b/app/src/main/java/top/fumiama/simpledict/SimpleProtobuf.java new file mode 100644 index 0000000..b635a01 --- /dev/null +++ b/app/src/main/java/top/fumiama/simpledict/SimpleProtobuf.java @@ -0,0 +1,84 @@ +package top.fumiama.simpledict; + +import android.util.Log; + +import org.jetbrains.annotations.NotNull; + +import java.util.Stack; + +public class SimpleProtobuf { + public static class Dict { + public byte[] key; + public byte[] data; + } + + private static final DictStack ds = new DictStack(); + + public static Dict[] getDictArray(@NotNull byte[] raw) { + int offset = 0; + SLLE s; + while (offset < raw.length) { + offset += getSLLE(raw, offset).len; //struct_len + offset += getSLLE(raw, offset).len; //type + s = getSLLE(raw, offset); //data len + Log.d("MySPB", "Data len:" + s.value); + Dict d = new Dict(); + d.key = new byte[s.value]; + offset += s.len; + System.arraycopy(raw, offset, d.key, 0, s.value); + offset += s.value; + offset += getSLLE(raw, offset).len; //type + s = getSLLE(raw, offset); //data len + Log.d("MySPB", "Data len:" + s.value); + d.data = new byte[s.value]; + offset += s.len; + System.arraycopy(raw, offset, d.data, 0, s.value); + offset += s.value; + ds.push(d); + } + return ds.popAllData(); + } + + @NotNull + private static SLLE getSLLE(byte[] p, int start) { + SLLE s = new SLLE(); + s.value = 0; + for (int i = 0; i < 4; i++) { + s.value += (p[start + i] & 0x7f) << (i * 7); + if ((p[start + i] & 0x80) == 0) { //无更高位 + s.len = i + 1; + break; + } + } + return s; + } + + private static class SLLE { + int value; + int len; + } + + private static class DictStack extends PopAllStack { + public Dict[] popAllData() { + Object[] t = popAll(); + if (t != null) { + Dict[] d = new Dict[t.length]; + for (int i = 0; i < t.length; i++) { + d[i] = (Dict) t[i]; + } + return d; + } else return null; + } + } + + private static class PopAllStack extends Stack { + public Object[] popAll() { + if (size() > 0) { + Object[] t = new Object[size()]; + System.arraycopy(elementData, 0, t, 0, size()); + setSize(0); + return t; + } else return null; + } + } +} diff --git a/build.gradle b/build.gradle index d421b7e..bcb9fa5 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.3' + classpath 'com.android.tools.build:gradle:4.2.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong