001: /*
002:
003: Derby - Class org.apache.derbyTesting.functionTests.util.streams.LoopingAlphabetStream
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derbyTesting.functionTests.util.streams;
023:
024: import java.io.InputStream;
025: import java.io.IOException;
026:
027: /**
028: * A stream returning a cycle of the 26 lowercase letters of the modern Latin
029: * alphabet.
030: */
031: public class LoopingAlphabetStream extends InputStream {
032:
033: /**
034: * Maximum size of buffer.
035: * Balance between size and memory usage.
036: */
037: private static final int MAX_BUF_SIZE = 32 * 1024;
038: private static final byte SPACE = (byte) ' ';
039:
040: /** Length of the stream. */
041: private final long length;
042: private final int trailingBlanks;
043: /** Remaining bytes in the stream. */
044: private long remainingBlanks;
045: private long remainingNonBlanks;
046: private byte[] buffer = new byte[0];
047: private final ByteAlphabet alphabet;
048:
049: /**
050: * Create a looping modern latin alphabet stream of the specified length.
051: *
052: * @param length the number of characters (and also the number of bytes)
053: */
054: public LoopingAlphabetStream(long length) {
055: this (length, 0);
056: }
057:
058: public LoopingAlphabetStream(long length, int trailingBlanks) {
059: this .length = length;
060: this .trailingBlanks = trailingBlanks;
061: this .remainingNonBlanks = length - trailingBlanks;
062: this .remainingBlanks = trailingBlanks;
063: this .alphabet = ByteAlphabet.modernLatinLowercase();
064: fillBuffer(alphabet.byteCount());
065: }
066:
067: /**
068: * Create a looping alphabet of the specified type and length.
069: *
070: * @param length the number of bytes in the stream
071: * @param alphabet the alphabet to loop over
072: */
073: public LoopingAlphabetStream(long length, ByteAlphabet alphabet) {
074: this (length, alphabet, 0);
075: }
076:
077: public LoopingAlphabetStream(long length, ByteAlphabet alphabet,
078: int trailingBlanks) {
079: this .length = length;
080: this .trailingBlanks = trailingBlanks;
081: this .remainingNonBlanks = length - trailingBlanks;
082: this .remainingBlanks = trailingBlanks;
083: this .alphabet = alphabet;
084: fillBuffer(alphabet.byteCount());
085: }
086:
087: public int read() {
088: if (remainingBlanks <= 0 && remainingNonBlanks <= 0) {
089: return -1;
090: }
091: if (remainingNonBlanks <= 0) {
092: remainingBlanks--;
093: return SPACE;
094: }
095: remainingNonBlanks--;
096: return alphabet.nextByte();
097: }
098:
099: public int read(byte[] buf, int off, int length) {
100: if (remainingBlanks <= 0 && remainingNonBlanks <= 0) {
101: return -1;
102: }
103: // We can only read as many bytes as there are in the stream.
104: int nonBlankLength = Math.min((int) remainingNonBlanks, length);
105: fillBuffer(nonBlankLength);
106: int read = 0;
107: // Find position of next letter in the buffer.
108: int bOff = alphabet.nextByteToRead(0);
109: if (nonBlankLength <= (buffer.length - bOff)) {
110: System.arraycopy(buffer, bOff, buf, off, nonBlankLength);
111: remainingNonBlanks -= nonBlankLength;
112: read = nonBlankLength;
113: alphabet.nextByteToRead(nonBlankLength);
114: } else {
115: // Must read several times from the buffer.
116: int toRead = 0;
117: while (remainingNonBlanks > 0 && read < nonBlankLength) {
118: bOff = alphabet.nextByteToRead(toRead);
119: toRead = Math.min(buffer.length - bOff, nonBlankLength
120: - read);
121: System.arraycopy(buffer, bOff, buf, off + read, toRead);
122: remainingNonBlanks -= toRead;
123: read += toRead;
124: }
125: bOff = alphabet.nextByteToRead(toRead);
126: }
127: if (read < length && remainingBlanks > 0) {
128: read += fillBlanks(buf, off + read, length - read);
129: }
130: return read;
131: }
132:
133: /**
134: * Reset the stream.
135: */
136: public void reset() {
137: remainingNonBlanks = length - trailingBlanks;
138: remainingBlanks = trailingBlanks;
139: alphabet.reset();
140: }
141:
142: /**
143: * Return remaining bytes in the stream.
144: */
145: public int available() {
146: return (int) (remainingNonBlanks + remainingBlanks);
147: }
148:
149: /**
150: * Fill internal buffer of bytes (from character sequence).
151: *
152: * @param bufSize the wanted size, might be ignored if too big
153: */
154: private void fillBuffer(int bufSize) {
155: if (bufSize > MAX_BUF_SIZE) {
156: bufSize = MAX_BUF_SIZE;
157: }
158: if (bufSize <= buffer.length) {
159: return;
160: }
161: int curOff = alphabet.nextByteToRead(0);
162: // First letter in buffer is always the first letter in the alphabet.
163: alphabet.reset();
164: buffer = new byte[bufSize];
165: for (int i = 0; i < bufSize; i++) {
166: buffer[i] = alphabet.nextByte();
167: }
168: // Must reset internal state of the alphabet, as we have not yet
169: // delivered any bytes.
170: alphabet.reset();
171: alphabet.nextByteToRead(curOff);
172: }
173:
174: private int fillBlanks(byte[] buf, int off, int length) {
175: int i = 0;
176: for (; i < length; i++) {
177: if (remainingBlanks > 0) {
178: buf[off + i] = SPACE;
179: remainingBlanks--;
180: } else {
181: break;
182: }
183: }
184: return i;
185: }
186: } // End class LoopingAlphabetStream
|