我在 clojure.java.io/copy 函数的实现中发现了一种奇怪的行为
https://github.com/clojure/clojure/blob/ee1b606ad066ac8df2efd4a6b8d0d365c206f5bf/src/clj/clojure/java/io.clj#L391
(defn copy
"Copies input to output. Returns nil or throws IOException.
Input may be an InputStream, Reader, File, byte[], char[], or String.
Output may be an OutputStream, Writer, or File.
Options are key/value pairs and may be one of
:buffer-size buffer size to use, default is 1024.
:encoding encoding to use if converting between
byte and char streams.
Does not close any streams except those it opens itself
(on a File)."
{:added "1.2"}
[input output & opts]
(do-copy input output (when opts (apply hash-map opts))))
实际上,拷贝操作在这里实现,当从 InputStream 拷贝到 OutputStream 时。
https://github.com/clojure/clojure/blob/ee1b606ad066ac8df2efd4a6b8d0d365c206f5bf/src/clj/clojure/java/io.clj#L306
(defmethod do-copy [InputStream OutputStream] [^InputStream input ^OutputStream output opts]
(let [buffer (make-array Byte/TYPE (buffer-size opts))]
(loop []
(let [size (.read input buffer)] ;;; XXX point 1
(when (pos? size) ;;; XXX point 2
(do (.write output buffer 0 size)
(recur)))))))
这里的位置 1 的 .read 函数为 https://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#read(byte[])
Java 文档中指出以下内容
从输入流中读取一些字节并将它们存储到缓冲数组 b 中。实际读取的字节数作为一个整数返回。该方法会在输入数据可用、检测到文件末尾或抛出异常时停止。
如果 b 的长度为零,则不会读取任何字节并返回 0;否则,尝试读取至少一个字节。如果没有可用的字节,因为流在文件末尾,则返回值 -1;否则,至少读取一个字节并将其存储到 b 中。
这意味着返回值 -1 表示流结束,而返回值 0 并不表示流结束。然而,上述代码位置 2 的条件,当 .read 返回的值小于 1 时,停止递归。
现在考虑一种情况,其中 .read 在连续调用中返回以下序列值
1024, 0, 1024, 201, -1
当整个流有 2249 字节时,clojure.java/io 仅拷贝前 1024 字节。这是预期的行为吗?应将位置 1 的条件更改为 (not (neg? size))
吗?
我将此问题也发布到了 google groups: https://groups.google.com/forum/#!topic/clojure/XzpPPXXhgM4