1 /**
2  * Copyright: Copyright Jason White, 2014-2016
3  * License:   $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4  * Authors:   Jason White
5  */
6 module io.stream;
7 
8 public import io.stream.types;
9 public import io.stream.traits;
10 public import io.stream.shim;
11 
12 import std.traits : isArray;
13 
14 /**
15  * Reads exactly the number of bytes requested from the stream. Throws
16  * an exception if it cannot be done. Returns the filled buffer.
17  *
18  * Note that, because it can potentially take multiple system calls to
19  * complete the read, the read is not guaranteed to be atomic with
20  * respect to other reads.
21  *
22  * Throws: ReadException if the given buffer cannot be completely filled.
23  */
24 T[] readExactly(Stream, T)(auto ref Stream stream, T[] buf)
25     if (isSource!(Stream))
26 {
27     ubyte[] byteBuf = cast(ubyte[])buf;
28 
29     size_t totalRead = 0;
30     while (totalRead < byteBuf.length)
31     {
32         if (immutable n = stream.read(byteBuf[totalRead .. $]))
33             totalRead += n;
34         else
35             throw new ReadException(
36                     "Failed to fill entire buffer from stream"
37                     );
38     }
39 
40     return buf;
41 }
42 
43 /**
44  * Writes exactly the given buffer and no less. Throws an exception if
45  * it cannot be done.
46  *
47  * Note that, because it can potentially take multiple system calls to
48  * complete the write, the write is not guaranteed to be atomic with
49  * respect to other writes.
50  *
51  * Throws: WriteException if the given buffer cannot be completely written.
52  */
53 void writeExactly(Stream, T)(auto ref Stream stream, in T[] buf)
54     if (isSink!Stream)
55 {
56     const(ubyte)[] byteBuf = cast(const(ubyte)[])buf;
57 
58     size_t total = 0;
59 
60     while (total < byteBuf.length)
61     {
62         if (immutable n = stream.write(byteBuf[total .. $]))
63             total += n;
64         else
65             throw new WriteException(
66                     "Failed to write entire buffer to stream"
67                     );
68     }
69 }
70 
71 // Ditto
72 void writeExactly(Stream, T)(auto ref Stream stream, const auto ref T value)
73     if (isSink!Stream && !isArray!T)
74 {
75     stream.writeExactly((cast(ubyte*)&value)[0 .. T.sizeof]);
76 }
77 
78 /**
79  * Reads the rest of the stream.
80  */
81 T[] readAll(T=ubyte, Stream)(auto ref Stream stream, long upTo = long.max)
82     if (isSource!Stream && isSeekable!Stream)
83 {
84     import std.algorithm : min;
85     import std.array : uninitializedArray;
86 
87     immutable remaining = min((stream.length - stream.position)/T.sizeof, upTo);
88 
89     auto buf = uninitializedArray!(T[])(remaining);
90 
91     immutable bytesRead = stream.read(buf);
92 
93     return buf[0 .. bytesRead/T.sizeof];
94 }
95 
96 /**
97  * Set the position (in bytes) of a stream.
98  */
99 @property void position(Stream)(auto ref Stream stream, long offset)
100     if (isSeekable!Stream)
101 {
102     stream.seekTo(offset, From.start);
103 }
104 
105 /**
106  * Gets the position (in bytes) of a stream.
107  */
108 @property auto position(Stream)(auto ref Stream stream)
109     if (isSeekable!Stream)
110 {
111     return stream.seekTo(0, From.here);
112 }
113 
114 /**
115  * Skip the specified number of bytes forward or backward.
116  *
117  * Returns: The position (in bytes) in the stream after the seek.
118  */
119 long skip(Stream)(auto ref Stream stream, long offset)
120     if (isSeekable!Stream)
121 {
122     return stream.seekTo(offset, From.here);
123 }