[PATCH 5/5] Support `FILE-POSITION` on string streams.

From cb667c106187443ff2d00bace14f0ee0686fe2fd Mon Sep 17 00:00:00 2001
Adds a custom, seekable writer to be able to go back in the written
output for `STRING-OUTPUT-STREAM` - the input case is slightly less
complex.
---
 build.xml                                          |   1 +
 src/org/armedbear/lisp/SeekableStringWriter.java   | 140 +++++++++++++++++++++
 src/org/armedbear/lisp/StringInputStream.java      |  43 ++++++-
 src/org/armedbear/lisp/StringOutputStream.java     |  35 +++++-
 test/lisp/abcl/misc-tests.lisp                     |  11 +-
 .../armedbear/lisp/SeekableStringWriterTest.java   |  19 +++
 6 files changed, 242 insertions(+), 7 deletions(-)
 create mode 100644 src/org/armedbear/lisp/SeekableStringWriter.java
 create mode 100644 test/src/org/armedbear/lisp/SeekableStringWriterTest.java
parent bcb6a6eb
......@@ -946,6 +946,7 @@ ${basedir}/../cl-bench
classname="org.junit.runner.JUnitCore">
<arg value="org.armedbear.lisp.PathnameTest"/>
<arg value="org.armedbear.lisp.StreamTest"/>
<arg value="org.armedbear.lisp.SeekableStringWriterTest"/>
<arg value="org.armedbear.lisp.UtilitiesTest"/>
<!-- currently hangs(!) the running process
<arg value="org.armedbear.lisp.util.HttpHeadTest"/>
......
/*
* SeekableStringWriter.java
*
* Copyright (C) 2016 Olof-Joachim Frahm
* $Id$
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under
* terms of your choice, provided that you also meet, for each linked
* independent module, the terms and conditions of the license of that
* module. An independent module is a module which is not derived from
* or based on this library. If you modify this library, you may extend
* this exception to your version of the library, but you are not
* obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
package org.armedbear.lisp;
import static org.armedbear.lisp.Lisp.*;
import java.io.Writer;
public final class SeekableStringWriter extends Writer {
private final StringBuffer stringBuffer;
private int offset = 0;
public SeekableStringWriter() {
stringBuffer = new StringBuffer();
}
public SeekableStringWriter(int initialSize) {
stringBuffer = new StringBuffer(initialSize);
}
public SeekableStringWriter append(char c) {
write(c);
return this;
}
public SeekableStringWriter append(CharSequence csq) {
write(csq.toString());
return this;
}
public SeekableStringWriter append(CharSequence csq, int start, int end) {
write(csq.subSequence(start, end).toString());
return this;
}
@Override
public void write(char[] cbuf) {
_write(cbuf, 0, cbuf.length);
}
@Override
public void write(char[] cbuf, int off, int len) {
int bufLen = cbuf.length;
if (off < 0 || off > bufLen || len < 0 || off + len > bufLen)
throw new IllegalArgumentException();
_write(cbuf, off, len);
}
@Override
public void write(int c) {
if (offset == stringBuffer.length())
stringBuffer.append((char) c);
else
stringBuffer.setCharAt(offset, (char) c);
++offset;
}
@Override
public void write(String str) {
write(str, 0, str.length());
}
@Override
public void write(String str, int off, int len) {
write(str.toCharArray(), off, len);
}
private void _write(char[] cbuf, int off, int len) {
int strLen = stringBuffer.length();
int space = strLen - offset;
int written = Math.min(len, space);
if (written > 0)
stringBuffer.replace(offset, offset + written, new String(cbuf, off, written));
if (written < len)
stringBuffer.append(cbuf, off + written, len - written);
offset += len;
}
public void seek(int offset) {
if (offset < 0 || offset > stringBuffer.length())
throw new IllegalArgumentException();
this.offset = offset;
}
public StringBuffer getBuffer() {
return stringBuffer;
}
public int getOffset() {
return offset;
}
@Override
public String toString() {
return stringBuffer.toString();
}
@Override
public void close() {}
@Override
public void flush() {}
}
......@@ -35,12 +35,14 @@ package org.armedbear.lisp;
import static org.armedbear.lisp.Lisp.*;
import java.io.IOException;
import java.io.StringReader;
public final class StringInputStream extends Stream
{
private final StringReader stringReader;
private final int start;
private final String subString;
public StringInputStream(String s)
{
......@@ -60,8 +62,9 @@ public final class StringInputStream extends Stream
eolStyle = EolStyle.RAW;
this.start = start;
stringReader = new StringReader(s.substring(start, end));
subString = s.substring(start, end);
stringReader = new StringReader(subString);
initAsCharacterInputStream(stringReader);
}
......@@ -93,7 +96,41 @@ public final class StringInputStream extends Stream
@Override
public int getOffset() {
return start + super.getOffset();
return start + offset;
}
@Override
protected long _getFilePosition() {
return getOffset();
}
@Override
protected boolean _setFilePosition(LispObject arg) {
try {
int offset;
if (arg == Keyword.START)
offset = 0;
else if (arg == Keyword.END)
offset = subString.length();
else {
long n = Fixnum.getValue(arg);
if (n < 0 || n > subString.length())
error(new StreamError(this, "FILE-POSITION got out of bounds argument."));
offset = (int) n; // FIXME arg might be a bignum
}
stringReader.reset();
stringReader.skip(offset);
initAsCharacterInputStream(stringReader);
this.offset = offset;
}
catch (IOException e) {
error(new StreamError(this, e));
}
return true;
}
// ### make-string-input-stream
......
......@@ -35,11 +35,12 @@ package org.armedbear.lisp;
import static org.armedbear.lisp.Lisp.*;
import java.io.IOException;
import java.io.StringWriter;
public final class StringOutputStream extends Stream
{
private final StringWriter stringWriter;
private final SeekableStringWriter stringWriter;
public StringOutputStream()
{
......@@ -51,7 +52,7 @@ public final class StringOutputStream extends Stream
super(Symbol.STRING_OUTPUT_STREAM);
this.elementType = elementType;
this.eolStyle = EolStyle.RAW;
initAsCharacterOutputStream(stringWriter = new StringWriter());
initAsCharacterOutputStream(stringWriter = new SeekableStringWriter());
}
@Override
......@@ -85,7 +86,35 @@ public final class StringOutputStream extends Stream
{
if (elementType == NIL)
return 0;
return stringWriter.getBuffer().length();
return offset;
}
@Override
protected boolean _setFilePosition(LispObject arg) {
if (elementType == NIL)
return false;
try {
int offset;
if (arg == Keyword.START)
offset = 0;
else if (arg == Keyword.END)
offset = stringWriter.getBuffer().length();
else {
long n = Fixnum.getValue(arg);
offset = (int) n; // FIXME arg might be a bignum
}
stringWriter.seek(offset);
this.offset = offset;
}
catch (IllegalArgumentException e) {
error(new StreamError(this, e));
}
return true;
}
public LispObject getString()
......
......@@ -108,4 +108,13 @@
(setf (values (mystruct-slot struct)
x)
(values 42 2))))))
42 2)
\ No newline at end of file
42 2)
(deftest string-output-stream.seekable
(string= "Goodbye, World! Something."
(let ((stream (make-string-output-stream)))
(write-string "Hello, World! Something." stream)
(file-position stream :start)
(write-string "Goodbye, World!" stream)
(get-output-stream-string stream)))
T)
package org.armedbear.lisp;
import static org.junit.Assert.*;
import org.junit.Test;
public class SeekableStringWriterTest
{
@Test
public void writeAndSeek() {
SeekableStringWriter writer = new SeekableStringWriter();
String buf = "sdf";
writer.append('a').append(buf).append(buf, 1, 2);
assertEquals("asdfd", writer.toString());
writer.seek(0);
writer.append("meow");
assertEquals("meowd", writer.toString());
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment