Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/java/nio/file/FileChannelLinesSpliterator.java
41159 views
1
/*
2
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
package java.nio.file;
26
27
import sun.nio.cs.ISO_8859_1;
28
import sun.nio.cs.UTF_8;
29
import sun.nio.cs.US_ASCII;
30
31
import java.io.BufferedReader;
32
import java.io.IOException;
33
import java.io.UncheckedIOException;
34
import java.nio.ByteBuffer;
35
import java.nio.channels.Channels;
36
import java.nio.channels.FileChannel;
37
import java.nio.channels.ReadableByteChannel;
38
import java.nio.charset.Charset;
39
import java.util.HashSet;
40
import java.util.Set;
41
import java.util.Spliterator;
42
import java.util.concurrent.atomic.AtomicInteger;
43
import java.util.function.Consumer;
44
45
import jdk.internal.access.SharedSecrets;
46
import jdk.internal.access.JavaNioAccess;
47
48
/**
49
* A file-based lines spliterator, leveraging a shared mapped byte buffer and
50
* associated file channel, covering lines of a file for character encodings
51
* where line feed characters can be easily identified from character encoded
52
* bytes.
53
*
54
* <p>
55
* When the root spliterator is first split a mapped byte buffer will be created
56
* over the file for it's size that was observed when the stream was created.
57
* Thus a mapped byte buffer is only required for parallel stream execution.
58
* Sub-spliterators will share that mapped byte buffer. Splitting will use the
59
* mapped byte buffer to find the closest line feed characters(s) to the left or
60
* right of the mid-point of covered range of bytes of the file. If a line feed
61
* is found then the spliterator is split with returned spliterator containing
62
* the identified line feed characters(s) at the end of it's covered range of
63
* bytes.
64
*
65
* <p>
66
* Traversing will create a buffered reader, derived from the file channel, for
67
* the range of bytes of the file. The lines are then read from that buffered
68
* reader. Once traversing commences no further splitting can be performed and
69
* the reference to the mapped byte buffer will be set to null.
70
*/
71
final class FileChannelLinesSpliterator implements Spliterator<String> {
72
73
static final Set<String> SUPPORTED_CHARSET_NAMES;
74
static {
75
SUPPORTED_CHARSET_NAMES = new HashSet<>();
76
SUPPORTED_CHARSET_NAMES.add(UTF_8.INSTANCE.name());
77
SUPPORTED_CHARSET_NAMES.add(ISO_8859_1.INSTANCE.name());
78
SUPPORTED_CHARSET_NAMES.add(US_ASCII.INSTANCE.name());
79
}
80
81
private final FileChannel fc;
82
private final Charset cs;
83
private int index;
84
private final int fence;
85
86
// Null before first split, non-null when splitting, null when traversing
87
private ByteBuffer buffer;
88
// Non-null when traversing
89
private BufferedReader reader;
90
91
// Number of references to the shared mapped buffer. Initialized to unity
92
// when the buffer is created by the root spliterator. Incremented in the
93
// sub-spliterator constructor. Decremented when 'buffer' transitions from
94
// non-null to null, either when traversing begins or if the spliterator is
95
// closed before traversal. If the count is zero after decrementing, then
96
// the buffer is unmapped.
97
private final AtomicInteger bufRefCount;
98
99
FileChannelLinesSpliterator(FileChannel fc, Charset cs, int index, int fence) {
100
this.fc = fc;
101
this.cs = cs;
102
this.index = index;
103
this.fence = fence;
104
this.bufRefCount = new AtomicInteger();
105
}
106
107
private FileChannelLinesSpliterator(FileChannel fc, Charset cs, int index,
108
int fence, ByteBuffer buffer, AtomicInteger bufRefCount) {
109
this.fc = fc;
110
this.cs = cs;
111
this.index = index;
112
this.fence = fence;
113
this.buffer = buffer;
114
this.bufRefCount = bufRefCount;
115
this.bufRefCount.incrementAndGet();
116
}
117
118
@Override
119
public boolean tryAdvance(Consumer<? super String> action) {
120
String line = readLine();
121
if (line != null) {
122
action.accept(line);
123
return true;
124
} else {
125
return false;
126
}
127
}
128
129
@Override
130
public void forEachRemaining(Consumer<? super String> action) {
131
String line;
132
while ((line = readLine()) != null) {
133
action.accept(line);
134
}
135
}
136
137
private BufferedReader getBufferedReader() {
138
/**
139
* A readable byte channel that reads bytes from an underlying
140
* file channel over a specified range.
141
*/
142
ReadableByteChannel rrbc = new ReadableByteChannel() {
143
@Override
144
public int read(ByteBuffer dst) throws IOException {
145
int bytesToRead = fence - index;
146
if (bytesToRead == 0)
147
return -1;
148
149
int bytesRead;
150
if (bytesToRead < dst.remaining()) {
151
// The number of bytes to read is less than remaining
152
// bytes in the buffer
153
// Snapshot the limit, reduce it, read, then restore
154
int oldLimit = dst.limit();
155
dst.limit(dst.position() + bytesToRead);
156
bytesRead = fc.read(dst, index);
157
dst.limit(oldLimit);
158
} else {
159
bytesRead = fc.read(dst, index);
160
}
161
if (bytesRead == -1) {
162
index = fence;
163
return bytesRead;
164
}
165
166
index += bytesRead;
167
return bytesRead;
168
}
169
170
@Override
171
public boolean isOpen() {
172
return fc.isOpen();
173
}
174
175
@Override
176
public void close() throws IOException {
177
fc.close();
178
}
179
};
180
return new BufferedReader(Channels.newReader(rrbc, cs.newDecoder(), -1));
181
}
182
183
private String readLine() {
184
if (reader == null) {
185
reader = getBufferedReader();
186
unmap();
187
}
188
189
try {
190
return reader.readLine();
191
} catch (IOException e) {
192
throw new UncheckedIOException(e);
193
}
194
}
195
196
private ByteBuffer getMappedByteBuffer() {
197
try {
198
return fc.map(FileChannel.MapMode.READ_ONLY, 0, fence);
199
} catch (IOException e) {
200
throw new UncheckedIOException(e);
201
}
202
}
203
204
@Override
205
public Spliterator<String> trySplit() {
206
// Cannot split after partial traverse
207
if (reader != null)
208
return null;
209
210
ByteBuffer b;
211
if ((b = buffer) == null) {
212
b = buffer = getMappedByteBuffer();
213
bufRefCount.set(1);
214
}
215
216
final int hi = fence, lo = index;
217
218
// Check if line separator hits the mid point
219
int mid = (lo + hi) >>> 1;
220
int c = b.get(mid);
221
if (c == '\n') {
222
mid++;
223
} else if (c == '\r') {
224
// Check if a line separator of "\r\n"
225
if (++mid < hi && b.get(mid) == '\n') {
226
mid++;
227
}
228
} else {
229
// TODO give up after a certain distance from the mid point?
230
// Scan to the left and right of the mid point
231
int midL = mid - 1;
232
int midR = mid + 1;
233
mid = 0;
234
while (midL > lo && midR < hi) {
235
// Sample to the left
236
c = b.get(midL--);
237
if (c == '\n' || c == '\r') {
238
// If c is "\r" then no need to check for "\r\n"
239
// since the subsequent value was previously checked
240
mid = midL + 2;
241
break;
242
}
243
244
// Sample to the right
245
c = b.get(midR++);
246
if (c == '\n' || c == '\r') {
247
mid = midR;
248
// Check if line-separator is "\r\n"
249
if (c == '\r' && mid < hi && b.get(mid) == '\n') {
250
mid++;
251
}
252
break;
253
}
254
}
255
}
256
257
// The left spliterator will have the line-separator at the end
258
return (mid > lo && mid < hi)
259
? new FileChannelLinesSpliterator(fc, cs, lo, index = mid,
260
b, bufRefCount)
261
: null;
262
}
263
264
@Override
265
public long estimateSize() {
266
// Use the number of bytes as an estimate.
267
// We could divide by a constant that is the average number of
268
// characters per-line, but that constant will be factored out.
269
return fence - index;
270
}
271
272
@Override
273
public long getExactSizeIfKnown() {
274
return -1;
275
}
276
277
@Override
278
public int characteristics() {
279
return Spliterator.ORDERED | Spliterator.NONNULL;
280
}
281
282
private void unmap() {
283
if (buffer != null) {
284
ByteBuffer b = buffer;
285
buffer = null;
286
if (bufRefCount.decrementAndGet() == 0) {
287
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
288
try {
289
nioAccess.unmapper(b).unmap();
290
} catch (UnsupportedOperationException ignored) {
291
}
292
}
293
}
294
}
295
296
void close() {
297
unmap();
298
}
299
}
300
301