157 lines
3.2 KiB
Python
157 lines
3.2 KiB
Python
# AGPLv3 or later
|
|
|
|
import json
|
|
import zlib
|
|
|
|
def encode(data, send_raw=False):
|
|
|
|
# This will encode data for sending.
|
|
# Data should be a writable into JSON.
|
|
|
|
if not send_raw:
|
|
J = json.dumps(data).encode("utf-8")
|
|
else:
|
|
J = data.encode("utf-8")
|
|
C = zlib.compress(J)
|
|
|
|
zed = len(C)
|
|
raw = len(J)
|
|
|
|
# Sometime compressed is larger than raw.
|
|
# We will add a byte saying 69 if it comressed
|
|
# and a byte saying 0 if it not sexy like this.
|
|
|
|
if not send_raw and zed < raw:
|
|
return chr(69).encode("utf-8") + C
|
|
else:
|
|
return chr(0).encode("utf-8") + J
|
|
|
|
def decode(data):
|
|
|
|
# This will decode the data
|
|
|
|
try:
|
|
code = data[0]
|
|
data = data[1:]
|
|
except:
|
|
code = 0
|
|
|
|
|
|
# 69 means the data is sexy
|
|
# see encode() for more info
|
|
|
|
if code == 69:
|
|
data = zlib.decompress(data).decode()
|
|
else:
|
|
data = data.decode()
|
|
|
|
# It should be Json but it could be something else
|
|
|
|
try:
|
|
return json.loads(data)
|
|
except:
|
|
return data
|
|
|
|
|
|
cache = {}
|
|
cache2 = {}
|
|
|
|
def diff(d1, d2, CHANGED=True):
|
|
|
|
# Function that checkes for differences
|
|
# between two dictionaries
|
|
|
|
ans = {}
|
|
|
|
# Comparing dictionaries
|
|
for i in d1:
|
|
if d1[i] != d2.get(i) and i in d2:
|
|
if not type(d2.get(i)) == dict:
|
|
ans[i] = d2[i]
|
|
else:
|
|
try:
|
|
ans[i] = diff(d1[i], d2[i], CHANGED=False)
|
|
except:
|
|
ans[i] = d2[i]
|
|
|
|
# Adding in new stuff
|
|
for i in d2:
|
|
if i not in d1:
|
|
ans[i] = d2[i]
|
|
|
|
# Telling it to remove old stuff
|
|
remove = []
|
|
for i in d1:
|
|
if i not in d2:
|
|
remove.append(i)
|
|
if remove:
|
|
ans["DELETED"] = remove
|
|
|
|
if ans and CHANGED:
|
|
ans["CHANGED"] = True
|
|
return ans
|
|
|
|
def combine(d1,d2):
|
|
|
|
# Function that combines 2 dictionaries
|
|
# based on the diff version.
|
|
|
|
# Changed parts
|
|
for i in d1:
|
|
if d1[i] != d2.get(i) and i in d2:
|
|
if not type(d2.get(i)) == dict:
|
|
d1[i] = d2[i]
|
|
else:
|
|
try:
|
|
combine(d1[i], d2[i])
|
|
except:
|
|
d1[i] = d2[i]
|
|
|
|
# Add new stuff
|
|
|
|
for i in d2:
|
|
|
|
if i in ("DELETED", "CHANGED"):
|
|
continue
|
|
|
|
if i not in d1:
|
|
d1[i] = d2[i]
|
|
|
|
# Deleting stuff
|
|
|
|
if "DELETED" in d2:
|
|
for i in d2["DELETED"]:
|
|
try:
|
|
del d1[i]
|
|
except:
|
|
pass
|
|
|
|
def encodeObj(data, ID, cache=cache):
|
|
|
|
# encodes Object, stripping it from all data
|
|
# that is unecessary
|
|
|
|
if ID not in cache:
|
|
cache[ID] = data
|
|
return encode(data)
|
|
|
|
else:
|
|
D = diff(cache[ID], data)
|
|
cache[ID] = data
|
|
return encode(D)
|
|
|
|
def decodeObj(data, ID, cache=cache):
|
|
data = decode(data)
|
|
if ID not in cache:
|
|
|
|
# TODO: implement the check to see
|
|
# if the data is actually CHANGED
|
|
# or not. And send back the request for the
|
|
# full thing if it is only the part.
|
|
|
|
cache[ID] = data
|
|
return data
|
|
else:
|
|
combine(cache[ID], data)
|
|
return cache[ID]
|
|
|