This repository has been archived by the owner on Jan 3, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
core.clj
123 lines (101 loc) · 3.76 KB
/
core.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
(ns tnetstrings.core
(:require [clojure.string :as str]))
;;;; Provides loads and dumps for conversion to and from typed netstrings
;;;; More on typed netstrings at http://www.tnetstrings.org
;;; Helper functions for loads
(defn- explode-tnetstring [s]
(if (or (neg? (.indexOf s (int \:))) (zero? (.length s)))
[:SHORTCOUNT :SHORTCOUNT s]
(try
(let [[len-str data-plus] (.split s ":" 2)
len (Integer. len-str)]
(if (or (neg? len) (> (+ len 1) (.length data-plus)))
[:SHORTCOUNT :SHORTCOUNT s]
(let
[data (.substring data-plus 0 len)
type (.charAt data-plus len)
remains (.substring data-plus (+ len 1))]
[data type remains])))
(catch java.lang.NumberFormatException e
[:INVALID :INVALID s]))))
(defn- invalid-or-short? [ & rest ]
(not (not-any? (fn [x] (or (= x :INVALID) (= x :SHORTCOUNT))) rest)))
;; prototyping for the benefit of load-list and load-map
(defn- load-item [data type] nil)
(defn- load-str [data]
data)
(defn- load-int [data]
(try
(Integer. data)
(catch java.lang.NumberFormatException e :INVALID)))
(defn- load-bool [data]
(cond (= data "true") true
(= data "false") false
:else :INVALID))
(defn- load-list [data]
(loop [data data accum []]
(if (str/blank? data)
(reverse accum)
(let [[data type remains] (explode-tnetstring data)
item (load-item data type)]
(if (invalid-or-short? item)
:INVALID ; shortcounts cannot happen inside of valid lists
(recur remains (cons item accum)))))))
(defn- load-map [data]
(loop [data data accum {}]
(if (str/blank? data)
accum
(let [[kdata ktype val-plus] (explode-tnetstring data)
[vdata vtype remains] (explode-tnetstring val-plus)
key (load-item kdata ktype)
val (load-item vdata vtype)]
(if (invalid-or-short? key val)
:INVALID ; shortcounts cannot happen inside of valid maps
(recur remains (assoc accum key val)))))))
(defn- load-item [data type]
(let [tis (fn [t] (= type t))] ; Abbreviating (= type t) to (tis t)
(cond (tis \~) nil ; Null
(tis \,) (load-str data) ; String
(tis \#) (load-int data) ; Integer
(tis \!) (load-bool data) ; Boolean
(tis \]) (load-list data) ; List
(tis \}) (load-map data) ; Map
(tis :SHORTCOUNT) :SHORTCOUNT ; Short Count
:else :INVALID)))
;;; Helper functions for dumps
;; prototyping for the benefit of dump-list and dump-map
(defn dump-item [item] nil)
(defn- dump-str [s]
(str (.length s) \: s \,))
(defn- dump-int [x]
(let [s (str x)]
(str (.length s) \: s \#)))
(defn- dump-bool [b]
(if b "4:true!" "5:false!"))
(defn- dump-list [lst]
(loop [lst lst accum ""]
(if (empty? lst)
(str (.length accum) \: accum \])
(recur (rest lst) (str accum (dump-item (first lst)))))))
(defn- dump-map [dict]
(loop [ks (keys dict) accum ""]
(if (empty? ks)
(str (.length accum) \: accum \})
(let [k (first ks) v (get dict k)]
(recur (rest ks)
(str accum (dump-item k) (dump-item v)))))))
(defn- boolean? [x] (or (= true x) (= false x)))
(defn- dump-item [item]
(cond (nil? item) "0:~" ; Null
(string? item) (dump-str item) ; String
(integer? item) (dump-int item) ; Integer
(boolean? item) (dump-bool item) ; Boolean
(list? item) (dump-list item) ; List
(map? item) (dump-map item) ; Map
:else :INVALID))
;;; Public Functions
(defn loads [s]
(let [[data type remains] (explode-tnetstring s)]
(load-item data type)))
(defn dumps [data]
(dump-item data))