Import miniabs 1.0 source
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
4fe9eed870
78 files changed
+5800
No files matched your search
@@ -0,0 +1,252 @@
|
||||
package com.miniabs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Small streaming JSON parser that builds org.json objects straight from the
|
||||
* socket. Very long strings (episode show notes) are cut short and bulky keys
|
||||
* are dropped while reading, so a podcast with hundreds of episodes still fits
|
||||
* in the Galaxy Mini's small heap.
|
||||
*/
|
||||
final class Json {
|
||||
/** Keys never kept, at any depth. */
|
||||
private static final String[] ALWAYS_SKIP = {"chapters", "libraryFiles", "metaTags", "rssFeed"};
|
||||
|
||||
private final Reader in;
|
||||
private final char[] buf = new char[8192];
|
||||
private int pos, len;
|
||||
private final int maxString;
|
||||
private final String[] deepSkip;
|
||||
private int depth;
|
||||
|
||||
private Json(Reader in, int maxString, String[] deepSkip) {
|
||||
this.in = in;
|
||||
this.maxString = maxString;
|
||||
this.deepSkip = deepSkip;
|
||||
}
|
||||
|
||||
static Object parse(Reader r) throws IOException {
|
||||
return parse(r, 2000, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxString longest string kept (longer ones end with an ellipsis)
|
||||
* @param deepSkip keys dropped inside objects nested three or more levels deep
|
||||
*/
|
||||
static Object parse(Reader r, int maxString, String[] deepSkip) throws IOException {
|
||||
Json j = new Json(r, maxString, deepSkip);
|
||||
int c = j.skipWs();
|
||||
if (c == -1) return null;
|
||||
return j.value(c);
|
||||
}
|
||||
|
||||
private int read() throws IOException {
|
||||
if (pos >= len) {
|
||||
len = in.read(buf, 0, buf.length);
|
||||
pos = 0;
|
||||
if (len <= 0) {
|
||||
len = 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return buf[pos++];
|
||||
}
|
||||
|
||||
private void unread() {
|
||||
pos--;
|
||||
}
|
||||
|
||||
private int skipWs() throws IOException {
|
||||
int c;
|
||||
do {
|
||||
c = read();
|
||||
} while (c == ' ' || c == '\n' || c == '\r' || c == '\t');
|
||||
return c;
|
||||
}
|
||||
|
||||
private Object value(int c) throws IOException {
|
||||
switch (c) {
|
||||
case '{':
|
||||
return object();
|
||||
case '[':
|
||||
return array();
|
||||
case '"':
|
||||
return string(maxString);
|
||||
case 't':
|
||||
expect("rue");
|
||||
return Boolean.TRUE;
|
||||
case 'f':
|
||||
expect("alse");
|
||||
return Boolean.FALSE;
|
||||
case 'n':
|
||||
expect("ull");
|
||||
return JSONObject.NULL;
|
||||
default:
|
||||
if (c == '-' || (c >= '0' && c <= '9')) return number(c);
|
||||
throw new IOException("Unexpected reply from server");
|
||||
}
|
||||
}
|
||||
|
||||
private void expect(String rest) throws IOException {
|
||||
for (int i = 0; i < rest.length(); i++) {
|
||||
if (read() != rest.charAt(i)) throw new IOException("Unexpected reply from server");
|
||||
}
|
||||
}
|
||||
|
||||
private Object number(int first) throws IOException {
|
||||
StringBuilder sb = new StringBuilder(16);
|
||||
sb.append((char) first);
|
||||
boolean dbl = false;
|
||||
int c;
|
||||
while (true) {
|
||||
c = read();
|
||||
if ((c >= '0' && c <= '9') || c == '-' || c == '+') {
|
||||
sb.append((char) c);
|
||||
} else if (c == '.' || c == 'e' || c == 'E') {
|
||||
dbl = true;
|
||||
sb.append((char) c);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c != -1) unread();
|
||||
String s = sb.toString();
|
||||
try {
|
||||
if (!dbl) {
|
||||
long l = Long.parseLong(s);
|
||||
if (l >= Integer.MIN_VALUE && l <= Integer.MAX_VALUE) return Integer.valueOf((int) l);
|
||||
return Long.valueOf(l);
|
||||
}
|
||||
return Double.valueOf(s);
|
||||
} catch (NumberFormatException e) {
|
||||
try {
|
||||
return Double.valueOf(s);
|
||||
} catch (NumberFormatException e2) {
|
||||
return Integer.valueOf(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean skipKey(String key) {
|
||||
for (String k : ALWAYS_SKIP) if (k.equals(key)) return true;
|
||||
if (deepSkip != null && depth >= 3) {
|
||||
for (String k : deepSkip) if (k.equals(key)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private JSONObject object() throws IOException {
|
||||
depth++;
|
||||
JSONObject o = new JSONObject();
|
||||
int c = skipWs();
|
||||
if (c == '}') {
|
||||
depth--;
|
||||
return o;
|
||||
}
|
||||
while (true) {
|
||||
if (c != '"') throw new IOException("Unexpected reply from server");
|
||||
String key = string(512);
|
||||
c = skipWs();
|
||||
if (c != ':') throw new IOException("Unexpected reply from server");
|
||||
c = skipWs();
|
||||
if (skipKey(key)) {
|
||||
skipValue(c);
|
||||
} else {
|
||||
Object v = value(c);
|
||||
try {
|
||||
o.put(key, v);
|
||||
} catch (JSONException ignored) {
|
||||
// NaN or similar; drop it
|
||||
}
|
||||
}
|
||||
c = skipWs();
|
||||
if (c == ',') {
|
||||
c = skipWs();
|
||||
continue;
|
||||
}
|
||||
if (c == '}') {
|
||||
depth--;
|
||||
return o;
|
||||
}
|
||||
throw new IOException("Unexpected reply from server");
|
||||
}
|
||||
}
|
||||
|
||||
private JSONArray array() throws IOException {
|
||||
JSONArray a = new JSONArray();
|
||||
int c = skipWs();
|
||||
if (c == ']') return a;
|
||||
while (true) {
|
||||
a.put(value(c));
|
||||
c = skipWs();
|
||||
if (c == ',') {
|
||||
c = skipWs();
|
||||
continue;
|
||||
}
|
||||
if (c == ']') return a;
|
||||
throw new IOException("Unexpected reply from server");
|
||||
}
|
||||
}
|
||||
|
||||
private void skipValue(int c) throws IOException {
|
||||
if (c == '"') {
|
||||
string(0);
|
||||
return;
|
||||
}
|
||||
if (c == '{' || c == '[') {
|
||||
int d = 1;
|
||||
while (d > 0) {
|
||||
int ch = read();
|
||||
if (ch == -1) throw new IOException("Reply was cut off");
|
||||
if (ch == '"') string(0);
|
||||
else if (ch == '{' || ch == '[') d++;
|
||||
else if (ch == '}' || ch == ']') d--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
value(c);
|
||||
}
|
||||
|
||||
private String string(int max) throws IOException {
|
||||
StringBuilder sb = new StringBuilder(max > 0 ? Math.min(max, 32) : 0);
|
||||
boolean cut = false;
|
||||
while (true) {
|
||||
int c = read();
|
||||
if (c == -1) throw new IOException("Reply was cut off");
|
||||
if (c == '"') break;
|
||||
if (c == '\\') {
|
||||
c = read();
|
||||
switch (c) {
|
||||
case 'n': c = '\n'; break;
|
||||
case 't': c = '\t'; break;
|
||||
case 'r': c = '\r'; break;
|
||||
case 'b': c = '\b'; break;
|
||||
case 'f': c = '\f'; break;
|
||||
case 'u': {
|
||||
int v = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int d = Character.digit(read(), 16);
|
||||
if (d < 0) throw new IOException("Unexpected reply from server");
|
||||
v = v * 16 + d;
|
||||
}
|
||||
c = v;
|
||||
break;
|
||||
}
|
||||
case -1:
|
||||
throw new IOException("Reply was cut off");
|
||||
default:
|
||||
break; // \" \\ \/
|
||||
}
|
||||
}
|
||||
if (sb.length() < max) sb.append((char) c);
|
||||
else cut = true;
|
||||
}
|
||||
if (cut && max > 0) sb.append('…');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user