package org.nutz.mvc.upload.util; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; class RingItem { byte[] buffer; int max; /** * 左标记,DUMP 时包含 */ int l; /** * 右标记,DUMP 时不包含 */ int r; /** * 下一次 Mark 是开始的位置 */ int nextmark; RingItem next; boolean isLoaded; boolean isStreamEnd; RingItem(int width) { this.buffer = new byte[width]; this.next = this; } RingItem createNext() { RingItem ri = new RingItem(buffer.length); ri.next = next; next = ri; return ri; } void load(InputStream ins) throws IOException { if (isLoaded) { throw new ReloadLoadedRingItemException(); } int bufferSize = buffer.length; max = ins.read(buffer, 0, bufferSize); // 流里不在有内容了 if (max < 0) { max = 0; isStreamEnd = true; } // 没有读全,继续读,直至read方法返回 -1, 或者读满. else { while (max < bufferSize) { int re = ins.read(buffer, max, bufferSize - max); if (re == -1) { isStreamEnd = true; break; } max += re; } } l = 0; r = 0; nextmark = 0; isLoaded = true; } void dump(OutputStream ops) throws IOException { if (l < r) { ops.write(buffer, l, r - l); } l = nextmark; r = l; isLoaded = (l < max); } /** * 试图从缓冲开头匹配,如果匹配失败,移动 'r' 并返回 false<br> * 如果匹配成功,则移动 'l'(匹配的内容不需要读取) 并返回 true * <p> * 这个函数,在 BufferRing 发现当前的环节点返回 '>0' 时,需要调用 next 的这个函数,看看是不是可以完整被匹配 * * @param bs * 数组 * @param offs * 偏移量 * @return 本节点开头是否匹配剩余的部分 */ boolean matchHeadingWithRemain(byte[] bs, int offs) { int i = 0; for (; offs < bs.length; offs++) { if (buffer[i++] != bs[offs]) { r = i; return false; } } // Matched, skip it l = i; r = i; nextmark = i; return true; } boolean isDone4Mark() { return nextmark == max; } /** * 从给定 offs 尽力匹配给出的数组。 * <p> * 需要注意的是,如果返回的是 >0 的数,内部的标志位将被设置到第一个匹配字符,以便 DUMP 内容。 <br> * 所以,如果下一个节点给出的结论是 -1,但是 'l' 并不是0,那么说明这个匹配是失败的,需要将 本节点的 r 置到 max 处。 * <p> * 返回值 * <ul> * <li><b>-1</b> - 全部被匹配 * <li><b>0</b> - 未发现匹配 * <li><b>大于 0</b> - 在缓冲的末尾发现匹配,但是没有匹配全,希望下一个节点继续从这个位置匹配 * </ul> * * @param bs * 数组 * @return -1, 0 或者 +n */ int mark(byte[] bs, int[] fails) { if (!isLoaded) throw new MarkUnloadedRingItemException(); byte start = bs[0]; for (; r < max; r++) { // 可能是边界,开始匹配 if (buffer[r] == start) { int re = 0; // 已经匹配长度 int j = r; // 在内容值字节数组中的指针 while (true) { re++; j++; // 全部匹配 if (re == bs.length) { nextmark = j; return -1; } // 到达本项目的结尾,但是并不确定是否是边界,因为还未匹配完 // 因此暂时假设这个不会被匹配 if (j == max) { nextmark = max; if (isStreamEnd) { r = max; return 0; } return re; } // 如果字符不相等,那么查看一下回退数组 // 如果回退到 0,则退出循环,因为这不是边界,否则继续循环匹配边界 if (bs[re] != buffer[j]) { re = fails[re]; // 再次判断回退后位置,如果还是不相同,则退出循环 if (bs[re] != buffer[j]) { break; } // 如果已经回退到了 0,你这么右边界置为 j,表示从头搜索 else if (re == 0) { r = j; } // 否则扩大边界,并继续循环 else { r += re == 0 ? 1 : re; } } } // make 'r' jump to 'j' r = j; } } // Fail to found nextmark = max; return 0; } }