001: /*
002: * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.jcr.myrrh;
030:
031: import com.caucho.vfs.*;
032:
033: import java.io.*;
034: import java.security.*;
035: import java.util.*;
036: import java.util.zip.*;
037:
038: /**
039: * Syntax: repeated data chunks of the following format.
040: *
041: * 4 - length of revision
042: * 20 - sha-1 of uncompressed
043: * 8 - local commit timestamp
044: * 4 - base offset (for delta)
045: * [...] - delta data
046: * 4 - crc-32
047: *
048: * delta:
049: * 00xxxxxx - add [0-63] bytes of data
050: * 01xxxxxx b1 b0 - copy [0-63] bytes of data with rel offset to source
051: * 10xxxxxx b0 - add upto 8k bytes of data
052: * 11xxxxxx b0 b3 b2 b1 b0- copy up to 8k bytes of data with rel offset
053: */
054: public class RevisionFile {
055: private final Path _path;
056: private boolean _isAllowCompress = true;
057:
058: public RevisionFile(Path path) {
059: _path = path;
060: }
061:
062: public void setAllowCompress(boolean allowCompress) {
063: _isAllowCompress = allowCompress;
064: }
065:
066: public byte[] appendFile(Path path, long timestamp)
067: throws IOException {
068: int fileLength = (int) path.getLength();
069:
070: TempOutputStream ts = new TempOutputStream();
071:
072: OutputStream os = ts;
073:
074: DeflaterOutputStream dOut = null;
075:
076: if (_isAllowCompress) {
077: dOut = new DeflaterOutputStream(os);
078: os = dOut;
079: }
080:
081: byte[] id = getSha1(path, timestamp);
082:
083: ReadStream in = path.openRead();
084: try {
085: in.writeToStream(os);
086: } finally {
087: in.close();
088: }
089:
090: if (dOut != null)
091: dOut.close();
092:
093: long offset = _path.getLength();
094: WriteStream out = _path.openAppend();
095:
096: try {
097: if (offset <= 0) {
098: out.write(0);
099: out.write(1);
100: offset = 2;
101: }
102:
103: CRC32 crc = new CRC32();
104: CheckedOutputStream crcOut = new CheckedOutputStream(out,
105: crc);
106:
107: int len = ts.getLength() + 37;
108:
109: writeInt(crcOut, len); // length
110:
111: crcOut.write(id);
112: writeLong(crcOut, timestamp);
113: writeInt(crcOut, 0); // no base
114:
115: if (_isAllowCompress)
116: crcOut.write('d');
117: else
118: crcOut.write(0);
119:
120: ts.writeToStream(crcOut);
121: ts.destroy();
122:
123: writeInt(out, (int) crc.getValue());
124:
125: crcOut.close();
126: } finally {
127: out.close();
128: }
129:
130: return id;
131: }
132:
133: private byte[] getSha1(Path path, long timestamp)
134: throws IOException {
135: ReadStream in = path.openRead();
136: try {
137: MessageDigest md = MessageDigest.getInstance("SHA1");
138:
139: md.update((byte) (timestamp >> 56));
140: md.update((byte) (timestamp >> 48));
141: md.update((byte) (timestamp >> 40));
142: md.update((byte) (timestamp >> 32));
143: md.update((byte) (timestamp >> 24));
144: md.update((byte) (timestamp >> 16));
145: md.update((byte) (timestamp >> 8));
146: md.update((byte) (timestamp));
147:
148: TempBuffer tempBuf = TempBuffer.allocate();
149: byte[] buffer = tempBuf.getBuffer();
150: int len;
151:
152: while ((len = in.read(buffer, 0, buffer.length)) > 0) {
153: md.update(buffer, 0, len);
154: }
155:
156: return md.digest();
157: } catch (NoSuchAlgorithmException e) {
158: throw new RuntimeException(e);
159: } finally {
160: in.close();
161: }
162: }
163:
164: private void writeInt(OutputStream out, int v) throws IOException {
165: out.write(v >> 24);
166: out.write(v >> 16);
167: out.write(v >> 8);
168: out.write(v);
169: }
170:
171: private void writeLong(OutputStream out, long v) throws IOException {
172: out.write((int) (v >> 56));
173: out.write((int) (v >> 48));
174: out.write((int) (v >> 40));
175: out.write((int) (v >> 32));
176: out.write((int) (v >> 24));
177: out.write((int) (v >> 16));
178: out.write((int) (v >> 8));
179: out.write((int) v);
180: }
181: }
|