comparison src/org/tmatesoft/hg/internal/RevlogStreamWriter.java @ 538:dd4f6311af52

Commit: first working version
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 05 Feb 2013 22:30:21 +0100
parents 243202f1bda5
children 9edfd5a223b8
comparison
equal deleted inserted replaced
537:5a455624be4f 538:dd4f6311af52
34 * @author Artem Tikhomirov 34 * @author Artem Tikhomirov
35 * @author TMate Software Ltd. 35 * @author TMate Software Ltd.
36 */ 36 */
37 public class RevlogStreamWriter { 37 public class RevlogStreamWriter {
38 38
39
40 /*XXX public because HgCloneCommand uses it*/
41 public static class HeaderWriter implements DataSerializer.DataSource {
42 private final ByteBuffer header;
43 private final boolean isInline;
44 private long offset;
45 private int length, compressedLength;
46 private int baseRev, linkRev, p1, p2;
47 private byte[] nodeid;
48
49 public HeaderWriter(boolean inline) {
50 isInline = inline;
51 header = ByteBuffer.allocate(REVLOGV1_RECORD_SIZE);
52 }
53
54 public HeaderWriter offset(long offset) {
55 this.offset = offset;
56 return this;
57 }
58
59 public int baseRevision() {
60 return baseRev;
61 }
62
63 public HeaderWriter baseRevision(int baseRevision) {
64 this.baseRev = baseRevision;
65 return this;
66 }
67
68 public HeaderWriter length(int len, int compressedLen) {
69 this.length = len;
70 this.compressedLength = compressedLen;
71 return this;
72 }
73
74 public HeaderWriter parents(int parent1, int parent2) {
75 p1 = parent1;
76 p2 = parent2;
77 return this;
78 }
79
80 public HeaderWriter linkRevision(int linkRevision) {
81 linkRev = linkRevision;
82 return this;
83 }
84
85 public HeaderWriter nodeid(Nodeid n) {
86 nodeid = n.toByteArray();
87 return this;
88 }
89
90 public HeaderWriter nodeid(byte[] nodeidBytes) {
91 nodeid = nodeidBytes;
92 return this;
93 }
94
95 public void serialize(DataSerializer out) throws IOException {
96 header.clear();
97 if (offset == 0) {
98 int version = 1 /* RevlogNG */;
99 if (isInline) {
100 final int INLINEDATA = 1 << 16; // FIXME extract constant
101 version |= INLINEDATA;
102 }
103 header.putInt(version);
104 header.putInt(0);
105 } else {
106 header.putLong(offset << 16);
107 }
108 header.putInt(compressedLength);
109 header.putInt(length);
110 header.putInt(baseRev);
111 header.putInt(linkRev);
112 header.putInt(p1);
113 header.putInt(p2);
114 header.put(nodeid);
115 // assume 12 bytes left are zeros
116 out.write(header.array(), 0, header.capacity());
117
118 // regardless whether it's inline or separate data,
119 // offset field always represent cumulative compressedLength
120 // (while offset in the index file with inline==true differs by n*sizeof(header), where n is entry's position in the file)
121 offset += compressedLength;
122 }
123
124 public int serializeLength() {
125 return header.capacity();
126 }
127 }
128
129 private final DigestHelper dh = new DigestHelper(); 39 private final DigestHelper dh = new DigestHelper();
130 private final RevlogCompressor revlogDataZip; 40 private final RevlogCompressor revlogDataZip;
131
132
133 public RevlogStreamWriter(SessionContext ctx, RevlogStream stream) {
134 revlogDataZip = new RevlogCompressor(ctx);
135 }
136
137 private int lastEntryBase, lastEntryIndex; 41 private int lastEntryBase, lastEntryIndex;
138 private byte[] lastEntryContent; 42 private byte[] lastEntryContent;
139 private Nodeid lastEntryRevision; 43 private Nodeid lastEntryRevision;
140 private IntMap<Nodeid> revisionCache = new IntMap<Nodeid>(32); 44 private IntMap<Nodeid> revisionCache = new IntMap<Nodeid>(32);
141 45 private RevlogStream revlogStream;
142 public void addRevision(byte[] content, int linkRevision, int p1, int p2) { 46
47 public RevlogStreamWriter(SessionContext ctx, RevlogStream stream) {
48 assert ctx != null;
49 assert stream != null;
50
51 revlogDataZip = new RevlogCompressor(ctx);
52 revlogStream = stream;
53 }
54
55 /**
56 * @return nodeid of added revision
57 */
58 public Nodeid addRevision(byte[] content, int linkRevision, int p1, int p2) {
59 lastEntryRevision = Nodeid.NULL;
143 int revCount = revlogStream.revisionCount(); 60 int revCount = revlogStream.revisionCount();
144 lastEntryIndex = revCount == 0 ? NO_REVISION : revCount - 1; 61 lastEntryIndex = revCount == 0 ? NO_REVISION : revCount - 1;
145 populateLastEntry(); 62 populateLastEntry();
146 // 63 //
147 PatchGenerator pg = new PatchGenerator(); 64 PatchGenerator pg = new PatchGenerator();
150 67
151 final boolean writeComplete = preferCompleteOverPatch(patchSerializedLength, content.length); 68 final boolean writeComplete = preferCompleteOverPatch(patchSerializedLength, content.length);
152 DataSerializer.DataSource dataSource = writeComplete ? new DataSerializer.ByteArrayDataSource(content) : patch.new PatchDataSource(); 69 DataSerializer.DataSource dataSource = writeComplete ? new DataSerializer.ByteArrayDataSource(content) : patch.new PatchDataSource();
153 revlogDataZip.reset(dataSource); 70 revlogDataZip.reset(dataSource);
154 final int compressedLen; 71 final int compressedLen;
155 final boolean useUncompressedData = preferCompressedOverComplete(revlogDataZip.getCompressedLength(), dataSource.serializeLength()); 72 final boolean useCompressedData = preferCompressedOverComplete(revlogDataZip.getCompressedLength(), dataSource.serializeLength());
156 if (useUncompressedData) { 73 if (useCompressedData) {
74 compressedLen= revlogDataZip.getCompressedLength();
75 } else {
157 // compression wasn't too effective, 76 // compression wasn't too effective,
158 compressedLen = dataSource.serializeLength() + 1 /*1 byte for 'u' - uncompressed prefix byte*/; 77 compressedLen = dataSource.serializeLength() + 1 /*1 byte for 'u' - uncompressed prefix byte*/;
159 } else {
160 compressedLen= revlogDataZip.getCompressedLength();
161 } 78 }
162 // 79 //
163 Nodeid p1Rev = revision(p1); 80 Nodeid p1Rev = revision(p1);
164 Nodeid p2Rev = revision(p2); 81 Nodeid p2Rev = revision(p2);
165 byte[] revisionNodeidBytes = dh.sha1(p1Rev, p2Rev, content).asBinary(); 82 byte[] revisionNodeidBytes = dh.sha1(p1Rev, p2Rev, content).asBinary();
175 revlogHeader.length(content.length, compressedLen); 92 revlogHeader.length(content.length, compressedLen);
176 revlogHeader.nodeid(revisionNodeidBytes); 93 revlogHeader.nodeid(revisionNodeidBytes);
177 revlogHeader.linkRevision(linkRevision); 94 revlogHeader.linkRevision(linkRevision);
178 revlogHeader.parents(p1, p2); 95 revlogHeader.parents(p1, p2);
179 revlogHeader.baseRevision(writeComplete ? lastEntryIndex+1 : lastEntryBase); 96 revlogHeader.baseRevision(writeComplete ? lastEntryIndex+1 : lastEntryBase);
97 revlogHeader.offset(revlogStream.newEntryOffset());
180 // 98 //
181 revlogHeader.serialize(indexFile); 99 revlogHeader.serialize(indexFile);
182 100
183 if (isInlineData) { 101 if (isInlineData) {
184 dataFile = indexFile; 102 dataFile = indexFile;
185 } else { 103 } else {
186 dataFile = revlogStream.getDataStreamWriter(); 104 dataFile = revlogStream.getDataStreamWriter();
187 } 105 }
188 activeFile = dataFile; 106 activeFile = dataFile;
189 if (useUncompressedData) { 107 if (useCompressedData) {
190 dataFile.writeByte((byte) 'u');
191 dataSource.serialize(dataFile);
192 } else {
193 int actualCompressedLenWritten = revlogDataZip.writeCompressedData(dataFile); 108 int actualCompressedLenWritten = revlogDataZip.writeCompressedData(dataFile);
194 if (actualCompressedLenWritten != compressedLen) { 109 if (actualCompressedLenWritten != compressedLen) {
195 throw new HgInvalidStateException(String.format("Expected %d bytes of compressed data, but actually wrote %d in %s", compressedLen, actualCompressedLenWritten, revlogStream.getDataFileName())); 110 throw new HgInvalidStateException(String.format("Expected %d bytes of compressed data, but actually wrote %d in %s", compressedLen, actualCompressedLenWritten, revlogStream.getDataFileName()));
196 } 111 }
112 } else {
113 dataFile.writeByte((byte) 'u');
114 dataSource.serialize(dataFile);
197 } 115 }
198 116
199 lastEntryContent = content; 117 lastEntryContent = content;
200 lastEntryBase = revlogHeader.baseRevision(); 118 lastEntryBase = revlogHeader.baseRevision();
201 lastEntryIndex++; 119 lastEntryIndex++;
213 indexFile.done(); 131 indexFile.done();
214 if (dataFile != null && dataFile != indexFile) { 132 if (dataFile != null && dataFile != indexFile) {
215 dataFile.done(); 133 dataFile.done();
216 } 134 }
217 } 135 }
218 } 136 return lastEntryRevision;
219 137 }
220 private RevlogStream revlogStream; 138
221 private Nodeid revision(int revisionIndex) { 139 private Nodeid revision(int revisionIndex) {
222 if (revisionIndex == NO_REVISION) { 140 if (revisionIndex == NO_REVISION) {
223 return Nodeid.NULL; 141 return Nodeid.NULL;
224 } 142 }
225 Nodeid n = revisionCache.get(revisionIndex); 143 Nodeid n = revisionCache.get(revisionIndex);
266 184
267 // true if length obtained with effort is worth it 185 // true if length obtained with effort is worth it
268 private static boolean decideWorthEffort(int lengthWithExtraEffort, int lengthWithoutEffort) { 186 private static boolean decideWorthEffort(int lengthWithExtraEffort, int lengthWithoutEffort) {
269 return lengthWithExtraEffort < (/* 3/4 of original */lengthWithoutEffort - (lengthWithoutEffort >>> 2)); 187 return lengthWithExtraEffort < (/* 3/4 of original */lengthWithoutEffort - (lengthWithoutEffort >>> 2));
270 } 188 }
189
190 /*XXX public because HgCloneCommand uses it*/
191 public static class HeaderWriter implements DataSerializer.DataSource {
192 private final ByteBuffer header;
193 private final boolean isInline;
194 private long offset;
195 private int length, compressedLength;
196 private int baseRev, linkRev, p1, p2;
197 private byte[] nodeid;
198
199 public HeaderWriter(boolean inline) {
200 isInline = inline;
201 header = ByteBuffer.allocate(REVLOGV1_RECORD_SIZE);
202 }
203
204 public HeaderWriter offset(long offset) {
205 this.offset = offset;
206 return this;
207 }
208
209 public int baseRevision() {
210 return baseRev;
211 }
212
213 public HeaderWriter baseRevision(int baseRevision) {
214 this.baseRev = baseRevision;
215 return this;
216 }
217
218 public HeaderWriter length(int len, int compressedLen) {
219 this.length = len;
220 this.compressedLength = compressedLen;
221 return this;
222 }
223
224 public HeaderWriter parents(int parent1, int parent2) {
225 p1 = parent1;
226 p2 = parent2;
227 return this;
228 }
229
230 public HeaderWriter linkRevision(int linkRevision) {
231 linkRev = linkRevision;
232 return this;
233 }
234
235 public HeaderWriter nodeid(Nodeid n) {
236 nodeid = n.toByteArray();
237 return this;
238 }
239
240 public HeaderWriter nodeid(byte[] nodeidBytes) {
241 nodeid = nodeidBytes;
242 return this;
243 }
244
245 public void serialize(DataSerializer out) throws IOException {
246 header.clear();
247 if (offset == 0) {
248 int version = 1 /* RevlogNG */;
249 if (isInline) {
250 final int INLINEDATA = 1 << 16; // FIXME extract constant
251 version |= INLINEDATA;
252 }
253 header.putInt(version);
254 header.putInt(0);
255 } else {
256 header.putLong(offset << 16);
257 }
258 header.putInt(compressedLength);
259 header.putInt(length);
260 header.putInt(baseRev);
261 header.putInt(linkRev);
262 header.putInt(p1);
263 header.putInt(p2);
264 header.put(nodeid);
265 // assume 12 bytes left are zeros
266 out.write(header.array(), 0, header.capacity());
267
268 // regardless whether it's inline or separate data,
269 // offset field always represent cumulative compressedLength
270 // (while offset in the index file with inline==true differs by n*sizeof(header), where n is entry's position in the file)
271 offset += compressedLength;
272 }
273
274 public int serializeLength() {
275 return header.capacity();
276 }
277 }
271 } 278 }