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