1 /*
2  * Copyright 2015 The Netty Project
3  *
4  * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
5  * "License"); you may not use this file except in compliance with the License. You may obtain a
6  * copy of the License at:
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software distributed under the License
11  * is distributed on an "AS IS" ~BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12  * or implied. See the License for the specific language governing permissions and limitations under
13  * the License.
14  */
15 module hunt.net.buffer.ByteProcessor;
16 
17 import std.concurrency : initOnce;
18 
19 /**
20  * Provides a mechanism to iterate over a collection of bytes.
21  */
22 interface ByteProcessor {
23 
24     enum byte SPACE = ' ';
25     enum byte HTAB = '\t';
26     enum byte CARRIAGE_RETURN = '\r';
27     enum byte LINE_FEED = '\n';
28 
29     /**
30      * Aborts on a {@code NUL (0x00)}.
31      */
32     static ByteProcessor FIND_NUL() {
33         __gshared ByteProcessor inst;
34         return initOnce!inst(new IndexOfProcessor(cast(byte) 0));
35     }
36 
37     /**
38      * Aborts on a non-{@code NUL (0x00)}.
39      */
40     static ByteProcessor FIND_NON_NUL() {
41         __gshared ByteProcessor inst;
42         return initOnce!inst(new IndexNotOfProcessor(cast(byte) 0));
43     }
44 
45     /**
46      * Aborts on a {@code CR ('\r')}.
47      */
48     static ByteProcessor FIND_CR() {
49         __gshared ByteProcessor inst;
50         return initOnce!inst(new IndexOfProcessor(CARRIAGE_RETURN));
51     }
52 
53     /**
54      * Aborts on a non-{@code CR ('\r')}.
55      */
56     static ByteProcessor FIND_NON_CR() {
57         __gshared ByteProcessor inst;
58         return initOnce!inst(new IndexNotOfProcessor(CARRIAGE_RETURN));
59     }
60 
61     /**
62      * Aborts on a {@code LF ('\n')}.
63      */
64     static ByteProcessor FIND_LF() {
65         __gshared ByteProcessor inst;
66         return initOnce!inst(new IndexOfProcessor(LINE_FEED));
67     }
68 
69     /**
70      * Aborts on a non-{@code LF ('\n')}.
71      */
72     static ByteProcessor FIND_NON_LF() {
73         __gshared ByteProcessor inst;
74         return initOnce!inst(new IndexNotOfProcessor(LINE_FEED));
75     }
76 
77     /**
78      * Aborts on a semicolon {@code (';')}.
79      */
80     static ByteProcessor FIND_SEMI_COLON() {
81         __gshared ByteProcessor inst;
82         return initOnce!inst(new IndexOfProcessor(cast(byte) ';'));
83     }
84 
85     /**
86      * Aborts on a comma {@code (',')}.
87      */
88     static ByteProcessor FIND_COMMA() {
89         __gshared ByteProcessor inst;
90         return initOnce!inst(new IndexOfProcessor(cast(byte) ','));
91     }
92 
93     /**
94      * Aborts on a ascii space character ({@code ' '}).
95      */
96     static ByteProcessor FIND_ASCII_SPACE() {
97         __gshared ByteProcessor inst;
98         return initOnce!inst(new IndexOfProcessor(SPACE));
99     }
100 
101     /**
102      * Aborts on a {@code CR ('\r')} or a {@code LF ('\n')}.
103      */
104     static ByteProcessor FIND_CRLF() {
105         __gshared ByteProcessor inst;
106         return initOnce!inst(new class ByteProcessor {
107             bool process(byte value) {
108                 return value != CARRIAGE_RETURN && value != LINE_FEED;
109             }
110         });
111     }
112 
113     /**
114      * Aborts on a byte which is neither a {@code CR ('\r')} nor a {@code LF ('\n')}.
115      */
116     static ByteProcessor FIND_NON_CRLF() {
117         __gshared ByteProcessor inst;
118         return initOnce!inst(new class ByteProcessor {
119             bool process(byte value) {
120                 return value == CARRIAGE_RETURN || value == LINE_FEED;
121             }
122         });
123     }
124 
125     /**
126      * Aborts on a linear whitespace (a ({@code ' '} or a {@code '\t'}).
127      */
128     static ByteProcessor FIND_LINEAR_WHITESPACE() {
129         __gshared ByteProcessor inst;
130         return initOnce!inst(new class ByteProcessor {
131             bool process(byte value) {
132                 return value != SPACE && value != HTAB;
133             }
134         });
135     }
136 
137     /**
138      * Aborts on a byte which is not a linear whitespace (neither {@code ' '} nor {@code '\t'}).
139      */
140     static ByteProcessor FIND_NON_LINEAR_WHITESPACE() {
141         __gshared ByteProcessor inst;
142         return initOnce!inst(new class ByteProcessor {
143             bool process(byte value) {
144                 return value == SPACE || value == HTAB;
145             }
146         });
147     }
148 
149     /**
150      * @return {@code true} if the processor wants to continue the loop and handle the next byte in the buffer.
151      *         {@code false} if the processor wants to stop handling bytes and abort the loop.
152      */
153     bool process(byte value);
154 }
155 
156 
157 /**
158  * A {@link ByteProcessor} which finds the first appearance of a specific byte.
159  */
160 class IndexOfProcessor : ByteProcessor {
161     private byte byteToFind;
162 
163     this(byte byteToFind) {
164         this.byteToFind = byteToFind;
165     }
166 
167     override
168     bool process(byte value) {
169         return value != byteToFind;
170     }
171 }
172 
173 /**
174  * A {@link ByteProcessor} which finds the first appearance which is not of a specific byte.
175  */
176 class IndexNotOfProcessor : ByteProcessor {
177     private byte byteToNotFind;
178 
179     this(byte byteToNotFind) {
180         this.byteToNotFind = byteToNotFind;
181     }
182 
183     override
184     bool process(byte value) {
185         return value == byteToNotFind;
186     }
187 }