13 import java.util.Base64 |
13 import java.util.Base64 |
14 |
14 |
15 import org.tukaani.xz.{XZInputStream, XZOutputStream} |
15 import org.tukaani.xz.{XZInputStream, XZOutputStream} |
16 |
16 |
17 |
17 |
18 object Bytes |
18 object Bytes { |
19 { |
|
20 val empty: Bytes = new Bytes(Array[Byte](), 0, 0) |
19 val empty: Bytes = new Bytes(Array[Byte](), 0, 0) |
21 |
20 |
22 def apply(s: CharSequence): Bytes = |
21 def apply(s: CharSequence): Bytes = { |
23 { |
|
24 val str = s.toString |
22 val str = s.toString |
25 if (str.isEmpty) empty |
23 if (str.isEmpty) empty |
26 else { |
24 else { |
27 val b = UTF8.bytes(str) |
25 val b = UTF8.bytes(str) |
28 new Bytes(b, 0, b.length) |
26 new Bytes(b, 0, b.length) |
42 val newline: Bytes = apply("\n") |
40 val newline: Bytes = apply("\n") |
43 |
41 |
44 |
42 |
45 /* base64 */ |
43 /* base64 */ |
46 |
44 |
47 def base64(s: String): Bytes = |
45 def base64(s: String): Bytes = { |
48 { |
|
49 val a = Base64.getDecoder.decode(s) |
46 val a = Base64.getDecoder.decode(s) |
50 new Bytes(a, 0, a.length) |
47 new Bytes(a, 0, a.length) |
51 } |
48 } |
52 |
49 |
53 object Decode_Base64 extends Scala.Fun("decode_base64") |
50 object Decode_Base64 extends Scala.Fun("decode_base64") { |
54 { |
|
55 val here = Scala_Project.here |
51 val here = Scala_Project.here |
56 def invoke(args: List[Bytes]): List[Bytes] = |
52 def invoke(args: List[Bytes]): List[Bytes] = |
57 List(base64(Library.the_single(args).text)) |
53 List(base64(Library.the_single(args).text)) |
58 } |
54 } |
59 |
55 |
60 object Encode_Base64 extends Scala.Fun("encode_base64") |
56 object Encode_Base64 extends Scala.Fun("encode_base64") { |
61 { |
|
62 val here = Scala_Project.here |
57 val here = Scala_Project.here |
63 def invoke(args: List[Bytes]): List[Bytes] = |
58 def invoke(args: List[Bytes]): List[Bytes] = |
64 List(Bytes(Library.the_single(args).base64)) |
59 List(Bytes(Library.the_single(args).base64)) |
65 } |
60 } |
66 |
61 |
83 } |
78 } |
84 |
79 |
85 new Bytes(out.toByteArray, 0, out.size) |
80 new Bytes(out.toByteArray, 0, out.size) |
86 } |
81 } |
87 |
82 |
88 def read(file: JFile): Bytes = |
83 def read(file: JFile): Bytes = { |
89 { |
|
90 val length = file.length |
84 val length = file.length |
91 val limit = if (length < 0 || length > Integer.MAX_VALUE) Integer.MAX_VALUE else length.toInt |
85 val limit = if (length < 0 || length > Integer.MAX_VALUE) Integer.MAX_VALUE else length.toInt |
92 using(new FileInputStream(file))(read_stream(_, limit = limit)) |
86 using(new FileInputStream(file))(read_stream(_, limit = limit)) |
93 } |
87 } |
94 |
88 |
106 } |
100 } |
107 |
101 |
108 final class Bytes private( |
102 final class Bytes private( |
109 protected val bytes: Array[Byte], |
103 protected val bytes: Array[Byte], |
110 protected val offset: Int, |
104 protected val offset: Int, |
111 val length: Int) extends CharSequence |
105 val length: Int) extends CharSequence { |
112 { |
|
113 /* equality */ |
106 /* equality */ |
114 |
107 |
115 override def equals(that: Any): Boolean = |
108 override def equals(that: Any): Boolean = { |
116 { |
|
117 that match { |
109 that match { |
118 case other: Bytes => |
110 case other: Bytes => |
119 if (this eq other) true |
111 if (this eq other) true |
120 else if (length != other.length) false |
112 else if (length != other.length) false |
121 else (0 until length).forall(i => bytes(offset + i) == other.bytes(other.offset + i)) |
113 else (0 until length).forall(i => bytes(offset + i) == other.bytes(other.offset + i)) |
122 case _ => false |
114 case _ => false |
123 } |
115 } |
124 } |
116 } |
125 |
117 |
126 private lazy val hash: Int = |
118 private lazy val hash: Int = { |
127 { |
|
128 var h = 0 |
119 var h = 0 |
129 for (i <- offset until offset + length) { |
120 for (i <- offset until offset + length) { |
130 val b = bytes(i).asInstanceOf[Int] & 0xFF |
121 val b = bytes(i).asInstanceOf[Int] & 0xFF |
131 h = 31 * h + b |
122 h = 31 * h + b |
132 } |
123 } |
144 |
135 |
145 def iterator: Iterator[Byte] = |
136 def iterator: Iterator[Byte] = |
146 for (i <- (offset until (offset + length)).iterator) |
137 for (i <- (offset until (offset + length)).iterator) |
147 yield bytes(i) |
138 yield bytes(i) |
148 |
139 |
149 def array: Array[Byte] = |
140 def array: Array[Byte] = { |
150 { |
|
151 val a = new Array[Byte](length) |
141 val a = new Array[Byte](length) |
152 System.arraycopy(bytes, offset, a, 0, length) |
142 System.arraycopy(bytes, offset, a, 0, length) |
153 a |
143 a |
154 } |
144 } |
155 |
145 |
156 def text: String = UTF8.decode_permissive(this) |
146 def text: String = UTF8.decode_permissive(this) |
157 |
147 |
158 def base64: String = |
148 def base64: String = { |
159 { |
|
160 val b = |
149 val b = |
161 if (offset == 0 && length == bytes.length) bytes |
150 if (offset == 0 && length == bytes.length) bytes |
162 else Bytes(bytes, offset, length).bytes |
151 else Bytes(bytes, offset, length).bytes |
163 Base64.getEncoder.encodeToString(b) |
152 Base64.getEncoder.encodeToString(b) |
164 } |
153 } |
165 |
154 |
166 def maybe_base64: (Boolean, String) = |
155 def maybe_base64: (Boolean, String) = { |
167 { |
|
168 val s = text |
156 val s = text |
169 if (this == Bytes(s)) (false, s) else (true, base64) |
157 if (this == Bytes(s)) (false, s) else (true, base64) |
170 } |
158 } |
171 |
159 |
172 override def toString: String = "Bytes(" + length + ")" |
160 override def toString: String = "Bytes(" + length + ")" |
189 |
177 |
190 def charAt(i: Int): Char = |
178 def charAt(i: Int): Char = |
191 if (0 <= i && i < length) (bytes(offset + i).asInstanceOf[Int] & 0xFF).asInstanceOf[Char] |
179 if (0 <= i && i < length) (bytes(offset + i).asInstanceOf[Int] & 0xFF).asInstanceOf[Char] |
192 else throw new IndexOutOfBoundsException |
180 else throw new IndexOutOfBoundsException |
193 |
181 |
194 def subSequence(i: Int, j: Int): Bytes = |
182 def subSequence(i: Int, j: Int): Bytes = { |
195 { |
|
196 if (0 <= i && i <= j && j <= length) new Bytes(bytes, offset + i, j - i) |
183 if (0 <= i && i <= j && j <= length) new Bytes(bytes, offset + i, j - i) |
197 else throw new IndexOutOfBoundsException |
184 else throw new IndexOutOfBoundsException |
198 } |
185 } |
199 |
186 |
200 def trim_line: Bytes = |
187 def trim_line: Bytes = |
215 /* XZ data compression */ |
202 /* XZ data compression */ |
216 |
203 |
217 def uncompress(cache: XZ.Cache = XZ.Cache()): Bytes = |
204 def uncompress(cache: XZ.Cache = XZ.Cache()): Bytes = |
218 using(new XZInputStream(stream(), cache))(Bytes.read_stream(_, hint = length)) |
205 using(new XZInputStream(stream(), cache))(Bytes.read_stream(_, hint = length)) |
219 |
206 |
220 def compress(options: XZ.Options = XZ.options(), cache: XZ.Cache = XZ.Cache()): Bytes = |
207 def compress(options: XZ.Options = XZ.options(), cache: XZ.Cache = XZ.Cache()): Bytes = { |
221 { |
|
222 val result = new ByteArrayOutputStream(length) |
208 val result = new ByteArrayOutputStream(length) |
223 using(new XZOutputStream(result, options, cache))(write_stream(_)) |
209 using(new XZOutputStream(result, options, cache))(write_stream(_)) |
224 new Bytes(result.toByteArray, 0, result.size) |
210 new Bytes(result.toByteArray, 0, result.size) |
225 } |
211 } |
226 |
212 |
227 def maybe_compress(options: XZ.Options = XZ.options(), cache: XZ.Cache = XZ.Cache()) |
213 def maybe_compress( |
228 : (Boolean, Bytes) = |
214 options: XZ.Options = XZ.options(), |
229 { |
215 cache: XZ.Cache = XZ.Cache() |
|
216 ) : (Boolean, Bytes) = { |
230 val compressed = compress(options = options, cache = cache) |
217 val compressed = compress(options = options, cache = cache) |
231 if (compressed.length < length) (true, compressed) else (false, this) |
218 if (compressed.length < length) (true, compressed) else (false, this) |
232 } |
219 } |
233 } |
220 } |