1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.stream.IOUtils;
13 
14 import hunt.Exceptions;
15 import hunt.util.Common;
16 import hunt.stream.Common;
17 
18 /**
19  * IO Utilities. Provides stream handling utilities in singleton Threadpool
20  * implementation accessed by static members.
21  */
22 class IOUtils {
23 
24 	enum string CRLF = "\015\012";
25 
26 	enum byte[] CRLF_BYTES = ['\015', '\012'];
27 
28 	enum int bufferSize = 64 * 1024;
29 
30 	// static class Job : Runnable {
31 	// 	InputStream input;
32 	// 	OutputStream output;
33 	// 	Reader read;
34 	// 	Writer write;
35 
36 	// 	Job(InputStream input, OutputStream output) {
37 	// 		this.input = input;
38 	// 		this.output = output;
39 	// 		this.read = null;
40 	// 		this.write = null;
41 	// 	}
42 
43 	// 	Job(Reader read, Writer write) {
44 	// 		this.input = null;
45 	// 		this.output = null;
46 	// 		this.read = read;
47 	// 		this.write = write;
48 	// 	}
49 
50 	// 	/*
51 	// 	 * @see java.lang.Runnable#run()
52 	// 	 */
53 	// 	void run() {
54 	// 		try {
55 	// 			if (input != null)
56 	// 				copy(input, output, -1);
57 	// 			else
58 	// 				copy(read, write, -1);
59 	// 		} catch (IOException e) {
60 	// 			try {
61 	// 				if (output != null)
62 	// 					output.close();
63 	// 				if (write != null)
64 	// 					write.close();
65 	// 			} catch (IOException e2) {
66 
67 	// 			}
68 	// 		}
69 	// 	}
70 	// }
71 
72 	/**
73 	 * Copy Stream input to Stream output until EOF or exception.
74 	 * 
75 	 * @param input
76 	 *            the input stream to read from (until EOF)
77 	 * @param output
78 	 *            the output stream to write to
79 	 * @throws IOException
80 	 *             if unable to copy streams
81 	 */
82 	static void copy(InputStream input, OutputStream output) {
83 		copy(input, output, -1);
84 	}
85 
86 	/**
87 	 * Copy Reader to Writer output until EOF or exception.
88 	 * 
89 	 * @param input
90 	 *            the read to read from (until EOF)
91 	 * @param output
92 	 *            the writer to write to
93 	 * @throws IOException
94 	 *             if unable to copy the streams
95 	 */
96 	// static void copy(Reader input, Writer output) {
97 	// 	copy(input, output, -1);
98 	// }
99 
100 	/**
101 	 * Copy Stream input to Stream for byteCount bytes or until EOF or exception.
102 	 * 
103 	 * @param input
104 	 *            the stream to read from
105 	 * @param output
106 	 *            the stream to write to
107 	 * @param byteCount
108 	 *            the number of bytes to copy
109 	 * @throws IOException
110 	 *             if unable to copy the streams
111 	 */
112 	static void copy(InputStream input, OutputStream output, long byteCount) {
113 		byte[] buffer = new byte[bufferSize];
114 		int len = bufferSize;
115 
116 		if (byteCount >= 0) {
117 			while (byteCount > 0) {
118 				int max = byteCount < bufferSize ? cast(int) byteCount : bufferSize;
119 				len = input.read(buffer, 0, max);
120 
121 				if (len == -1)
122 					break;
123 
124 				byteCount -= len;
125 				output.write(buffer, 0, len);
126 			}
127 		} else {
128 			while (true) {
129 				len = input.read(buffer, 0, bufferSize);
130 				if (len < 0)
131 					break;
132 				output.write(buffer, 0, len);
133 			}
134 		}
135 	}
136 
137 	/**
138 	 * Copy Reader to Writer for byteCount bytes or until EOF or exception.
139 	 * 
140 	 * @param input
141 	 *            the Reader to read from
142 	 * @param output
143 	 *            the Writer to write to
144 	 * @param byteCount
145 	 *            the number of bytes to copy
146 	 * @throws IOException
147 	 *             if unable to copy streams
148 	 */
149 	// static void copy(Reader input, Writer output, long byteCount) {
150 	// 	char[] buffer = new char[bufferSize];
151 	// 	int len = bufferSize;
152 
153 	// 	if (byteCount >= 0) {
154 	// 		while (byteCount > 0) {
155 	// 			if (byteCount < bufferSize)
156 	// 				len = input.read(buffer, 0, (int) byteCount);
157 	// 			else
158 	// 				len = input.read(buffer, 0, bufferSize);
159 
160 	// 			if (len == -1)
161 	// 				break;
162 
163 	// 			byteCount -= len;
164 	// 			output.write(buffer, 0, len);
165 	// 		}
166 	// 	} else if (output instanceof PrintWriter) {
167 	// 		PrintWriter pout = (PrintWriter) output;
168 	// 		while (!pout.checkError()) {
169 	// 			len = input.read(buffer, 0, bufferSize);
170 	// 			if (len == -1)
171 	// 				break;
172 	// 			output.write(buffer, 0, len);
173 	// 		}
174 	// 	} else {
175 	// 		while (true) {
176 	// 			len = input.read(buffer, 0, bufferSize);
177 	// 			if (len == -1)
178 	// 				break;
179 	// 			output.write(buffer, 0, len);
180 	// 		}
181 	// 	}
182 	// }
183 
184 	/**
185 	 * Copy files or directories
186 	 * 
187 	 * @param from
188 	 *            the file to copy
189 	 * @param to
190 	 *            the destination to copy to
191 	 * @throws IOException
192 	 *             if unable to copy
193 	 */
194 	// static void copy(File from, File to) {
195 	// 	if (from.isDirectory())
196 	// 		copyDir(from, to);
197 	// 	else
198 	// 		copyFile(from, to);
199 	// }
200 
201 	// static void copyDir(File from, File to) {
202 	// 	if (to.exists()) {
203 	// 		if (!to.isDirectory())
204 	// 			throw new IllegalArgumentException(to.toString());
205 	// 	} else
206 	// 		to.mkdirs();
207 
208 	// 	File[] files = from.listFiles();
209 	// 	if (files != null) {
210 	// 		for (int i = 0; i < files.length; i++) {
211 	// 			string name = files[i].getName();
212 	// 			if (".".equals(name) || "..".equals(name))
213 	// 				continue;
214 	// 			copy(files[i], new File(to, name));
215 	// 		}
216 	// 	}
217 	// }
218 
219 	// static void copyFile(File from, File to) {
220 	// 	try (InputStream input = new FileInputStream(from); OutputStream output = new FileOutputStream(to)) {
221 	// 		copy(input, output);
222 	// 	}
223 	// }
224 
225 	/**
226 	 * Read input stream to string.
227 	 * 
228 	 * @param input
229 	 *            the stream to read from (until EOF)
230 	 * @return the string parsed from stream (default Charset)
231 	 * @throws IOException
232 	 *             if unable to read the stream (or handle the charset)
233 	 */
234 	static string toString(InputStream input) {
235 		return toString(input, null);
236 	}
237 
238 	/**
239 	 * Read input stream to string.
240 	 * 
241 	 * @param input
242 	 *            the stream to read from (until EOF)
243 	 * @param encoding
244 	 *            the encoding to use (can be null to use default Charset)
245 	 * @return the string parsed from the stream
246 	 * @throws IOException
247 	 *             if unable to read the stream (or handle the charset)
248 	 */
249 	// static string toString(InputStream input, string encoding) {
250 	// 	return toString(input, encoding == null ? null : Charset.forName(encoding));
251 	// }
252 
253 	/**
254 	 * Read input stream to string.
255 	 * 
256 	 * @param input
257 	 *            the stream to read from (until EOF)
258 	 * @param encoding
259 	 *            the Charset to use (can be null to use default Charset)
260 	 * @return the string parsed from the stream
261 	 * @throws IOException
262 	 *             if unable to read the stream (or handle the charset)
263 	 */
264 	static string toString(InputStream input, string encoding) {
265 		import std.array;
266 		Appender!(string) sb;
267 		byte[] buffer = new byte[bufferSize];
268 		int len = bufferSize;
269 		while (true) {
270 			len = input.read(buffer, 0, bufferSize);
271 			if (len < 0)
272 				break;
273 			sb.put(cast(string)buffer[0..len]);
274 		}
275 		// StringWriter writer = new StringWriter();
276 		// InputStreamReader reader = encoding == null ? new InputStreamReader(input) : new InputStreamReader(input, encoding);
277 
278 		// copy(reader, writer);
279 		return sb.data;
280 	}
281 
282 	/**
283 	 * Read input stream to string.
284 	 * 
285 	 * @param input
286 	 *            the reader to read from (until EOF)
287 	 * @return the string parsed from the reader
288 	 * @throws IOException
289 	 *             if unable to read the stream (or handle the charset)
290 	 */
291 	// static string toString(Reader input) {
292 	// 	StringWriter writer = new StringWriter();
293 	// 	copy(input, writer);
294 	// 	return writer.toString();
295 	// }
296 
297 	/**
298 	 * Delete File. This delete will recursively delete directories - BE
299 	 * CAREFULL
300 	 * 
301 	 * @param file
302 	 *            The file (or directory) to be deleted.
303 	 * @return true if anything was deleted. (note: this does not mean that all
304 	 *         content input a directory was deleted)
305 	 */
306 	// static bool delete(File file) {
307 	// 	if (!file.exists())
308 	// 		return false;
309 	// 	if (file.isDirectory()) {
310 	// 		File[] files = file.listFiles();
311 	// 		for (int i = 0; files != null && i < files.length; i++)
312 	// 			delete(files[i]);
313 	// 	}
314 	// 	return file.delete();
315 	// }
316 
317 	/**
318 	 * Closes an arbitrary closable, and logs exceptions at ignore level
319 	 *
320 	 * @param closeable
321 	 *            the closeable to close
322 	 */
323 	static void close(Closeable closeable) {
324 		try {
325 			if (closeable !is null)
326 				closeable.close();
327 		} catch (IOException ignore) {
328 		}
329 	}
330 
331 	/**
332 	 * closes an input stream, and logs exceptions
333 	 *
334 	 * @param input
335 	 *            the input stream to close
336 	 */
337 	static void close(InputStream input) {
338 		close(cast(Closeable) input);
339 	}
340 
341 	/**
342 	 * closes an output stream, and logs exceptions
343 	 *
344 	 * @param os
345 	 *            the output stream to close
346 	 */
347 	static void close(OutputStream os) {
348 		close(cast(Closeable) os);
349 	}
350 
351 	// /**
352 	//  * closes a reader, and logs exceptions
353 	//  *
354 	//  * @param reader
355 	//  *            the reader to close
356 	//  */
357 	// static void close(Reader reader) {
358 	// 	close((Closeable) reader);
359 	// }
360 
361 	// /**
362 	//  * closes a writer, and logs exceptions
363 	//  *
364 	//  * @param writer
365 	//  *            the writer to close
366 	//  */
367 	// static void close(Writer writer) {
368 	// 	close((Closeable) writer);
369 	// }
370 
371 	// static byte[] readBytes(InputStream input) {
372 	// 	ByteArrayOutputStream bout = new ByteArrayOutputStream();
373 	// 	copy(input, bout);
374 	// 	return bout.toByteArray();
375 	// }
376 
377 	// /**
378 	//  * A gathering write utility wrapper.
379 	//  * <p>
380 	//  * This method wraps a gather write with a loop that handles the limitations
381 	//  * of some operating systems that have a limit on the number of buffers
382 	//  * written. The method loops on the write until either all the content is
383 	//  * written or no progress is made.
384 	//  *
385 	//  * @param output
386 	//  *            The GatheringByteChannel to write to
387 	//  * @param buffers
388 	//  *            The buffers to write
389 	//  * @param offset
390 	//  *            The offset into the buffers array
391 	//  * @param length
392 	//  *            The length input buffers to write
393 	//  * @return The total bytes written
394 	//  * @throws IOException
395 	//  *             if unable write to the GatheringByteChannel
396 	//  */
397 	// static long write(GatheringByteChannel output, ByteBuffer[] buffers, int offset, int length)
398 	// 		throws IOException {
399 	// 	long total = 0;
400 	// 	write: while (length > 0) {
401 	// 		// Write as much as we can
402 	// 		long wrote = output.write(buffers, offset, length);
403 
404 	// 		// If we can't write any more, give up
405 	// 		if (wrote == 0)
406 	// 			break;
407 
408 	// 		// count the total
409 	// 		total += wrote;
410 
411 	// 		// Look for unwritten content
412 	// 		for (int i = offset; i < buffers.length; i++) {
413 	// 			if (buffers[i].hasRemaining()) {
414 	// 				// loop with new offset and length;
415 	// 				length = length - (i - offset);
416 	// 				offset = i;
417 	// 				continue write;
418 	// 			}
419 	// 		}
420 	// 		length = 0;
421 	// 	}
422 
423 	// 	return total;
424 	// }
425 
426 	// /**
427 	//  * @return An outputstream to nowhere
428 	//  */
429 	// static OutputStream getNullStream() {
430 	// 	return __nullStream;
431 	// }
432 
433 	// /**
434 	//  * @return An outputstream to nowhere
435 	//  */
436 	// static InputStream getClosedStream() {
437 	// 	return __closedStream;
438 	// }
439 
440 	// private static class NullOS extends OutputStream {
441 	// 	override
442 	// 	void close() {
443 	// 	}
444 
445 	// 	override
446 	// 	void flush() {
447 	// 	}
448 
449 	// 	override
450 	// 	void write(byte[] b) {
451 	// 	}
452 
453 	// 	override
454 	// 	void write(byte[] b, int i, int l) {
455 	// 	}
456 
457 	// 	override
458 	// 	void write(int b) {
459 	// 	}
460 	// }
461 
462 	// private static NullOS __nullStream = new NullOS();
463 
464 	// private static class ClosedIS extends InputStream {
465 	// 	override
466 	// 	int read() {
467 	// 		return -1;
468 	// 	}
469 	// }
470 
471 	// private static ClosedIS __closedStream = new ClosedIS();
472 
473 	// /**
474 	//  * @return An writer to nowhere
475 	//  */
476 	// static Writer getNullWriter() {
477 	// 	return __nullWriter;
478 	// }
479 
480 	// /**
481 	//  * @return An writer to nowhere
482 	//  */
483 	// static PrintWriter getNullPrintWriter() {
484 	// 	return __nullPrintWriter;
485 	// }
486 
487 	// private static class NullWrite : Writer {
488 	// 	override
489 	// 	void close() {
490 	// 	}
491 
492 	// 	override
493 	// 	void flush() {
494 	// 	}
495 
496 	// 	override
497 	// 	void write(char[] b) {
498 	// 	}
499 
500 	// 	override
501 	// 	void write(char[] b, int o, int l) {
502 	// 	}
503 
504 	// 	override
505 	// 	void write(int b) {
506 	// 	}
507 
508 	// 	override
509 	// 	void write(string s) {
510 	// 	}
511 
512 	// 	override
513 	// 	void write(string s, int o, int l) {
514 	// 	}
515 	// }
516 
517 	// private static NullWrite __nullWriter = new NullWrite();
518 	// private static PrintWriter __nullPrintWriter = new PrintWriter(__nullWriter);
519 
520 }