comparison src/org/tmatesoft/hg/repo/HgDataFile.java @ 134:afac8ddc5dd2

Keep record if we tried and found no metadata for a given revision
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 16 Feb 2011 21:51:32 +0100
parents 4a948ec83980
children 3959bffb14e9
comparison
equal deleted inserted replaced
133:4a948ec83980 134:afac8ddc5dd2
41 41
42 // absolute from repo root? 42 // absolute from repo root?
43 // slashes, unix-style? 43 // slashes, unix-style?
44 // repo location agnostic, just to give info to user, not to access real storage 44 // repo location agnostic, just to give info to user, not to access real storage
45 private final Path path; 45 private final Path path;
46 private Metadata metadata; 46 private Metadata metadata; // get initialized on first access to file content.
47 47
48 /*package-local*/HgDataFile(HgRepository hgRepo, Path filePath, RevlogStream content) { 48 /*package-local*/HgDataFile(HgRepository hgRepo, Path filePath, RevlogStream content) {
49 super(hgRepo, content); 49 super(hgRepo, content);
50 path = filePath; 50 path = filePath;
51 } 51 }
98 public byte[] content(int revision) { 98 public byte[] content(int revision) {
99 if (revision == TIP) { 99 if (revision == TIP) {
100 revision = content.revisionCount() - 1; // FIXME maxRevision. 100 revision = content.revisionCount() - 1; // FIXME maxRevision.
101 } 101 }
102 byte[] data = super.content(revision); 102 byte[] data = super.content(revision);
103 if (data.length < 4 || (data[0] != 1 && data[1] != 10)) { 103 if (metadata == null) {
104 metadata = new Metadata();
105 }
106 if (metadata.none(revision)) {
107 // although not very reasonable when data is byte array, this check might
108 // get handy when there's a stream/channel to avoid useless reads and rewinds.
104 return data; 109 return data;
105 } 110 }
106 int toSkip = 0; 111 int toSkip = 0;
107 if (metadata == null || !metadata.known(revision)) { 112 if (!metadata.known(revision)) {
113 if (data.length < 4 || (data[0] != 1 && data[1] != 10)) {
114 metadata.recordNone(revision);
115 return data;
116 }
108 int lastEntryStart = 2; 117 int lastEntryStart = 2;
109 int lastColon = -1; 118 int lastColon = -1;
110 ArrayList<MetadataEntry> _metadata = new ArrayList<MetadataEntry>(); 119 ArrayList<MetadataEntry> _metadata = new ArrayList<MetadataEntry>();
111 String key = null, value = null; 120 String key = null, value = null;
112 for (int i = 2; i < data.length; i++) { 121 for (int i = 2; i < data.length; i++) {
131 lastEntryStart = i+1; 140 lastEntryStart = i+1;
132 break; 141 break;
133 } 142 }
134 } 143 }
135 _metadata.trimToSize(); 144 _metadata.trimToSize();
136 if (metadata == null) {
137 metadata = new Metadata();
138 }
139 metadata.add(revision, lastEntryStart, _metadata); 145 metadata.add(revision, lastEntryStart, _metadata);
140 toSkip = lastEntryStart; 146 toSkip = lastEntryStart;
141 } else { 147 } else {
142 toSkip = metadata.dataOffset(revision); 148 toSkip = metadata.dataOffset(revision);
143 } 149 }
185 int changelogRevision = getChangesetLocalRevision(getLocalRevision(nid)); 191 int changelogRevision = getChangesetLocalRevision(getLocalRevision(nid));
186 return getRepo().getChangelog().getRevision(changelogRevision); 192 return getRepo().getChangelog().getRevision(changelogRevision);
187 } 193 }
188 194
189 public boolean isCopy() { 195 public boolean isCopy() {
190 if (metadata == null) { 196 if (metadata == null || !metadata.checked(0)) {
197 // content() always initializes metadata.
191 content(0); // FIXME expensive way to find out metadata, distinct RevlogStream.Iterator would be better. 198 content(0); // FIXME expensive way to find out metadata, distinct RevlogStream.Iterator would be better.
192 } 199 }
193 if (metadata == null || !metadata.known(0)) { 200 if (!metadata.known(0)) {
194 return false; 201 return false;
195 } 202 }
196 return metadata.find(0, "copy") != null; 203 return metadata.find(0, "copy") != null;
197 } 204 }
198 205
227 valueStart = key.length(); 234 valueStart = key.length();
228 } 235 }
229 /*package-local*/boolean matchKey(String key) { 236 /*package-local*/boolean matchKey(String key) {
230 return key.length() == valueStart && entry.startsWith(key); 237 return key.length() == valueStart && entry.startsWith(key);
231 } 238 }
232 public String key() { 239 // uncomment once/if needed
233 return entry.substring(0, valueStart); 240 // public String key() {
234 } 241 // return entry.substring(0, valueStart);
242 // }
235 public String value() { 243 public String value() {
236 return entry.substring(valueStart); 244 return entry.substring(valueStart);
237 } 245 }
238 } 246 }
239 247
240 private static class Metadata { 248 private static class Metadata {
241 // XXX sparse array needed 249 // XXX sparse array needed
242 private final TreeMap<Integer, Integer> offsets = new TreeMap<Integer, Integer>(); 250 private final TreeMap<Integer, Integer> offsets = new TreeMap<Integer, Integer>();
243 private final TreeMap<Integer, MetadataEntry[]> entries = new TreeMap<Integer, MetadataEntry[]>(); 251 private final TreeMap<Integer, MetadataEntry[]> entries = new TreeMap<Integer, MetadataEntry[]>();
252
253 private final Integer NONE = new Integer(-1); // do not duplicate -1 integers at least within single file (don't want statics)
254
255 // true when there's metadata for given revision
244 boolean known(int revision) { 256 boolean known(int revision) {
257 Integer i = offsets.get(revision);
258 return i != null && NONE != i;
259 }
260
261 // true when revision has been checked for metadata presence.
262 public boolean checked(int revision) {
245 return offsets.containsKey(revision); 263 return offsets.containsKey(revision);
246 } 264 }
265
266 // true when revision has been checked and found not having any metadata
267 boolean none(int revision) {
268 Integer i = offsets.get(revision);
269 return i == NONE;
270 }
271
272 // mark revision as having no metadata.
273 void recordNone(int revision) {
274 Integer i = offsets.get(revision);
275 if (i == NONE) {
276 return; // already there
277 }
278 if (i != null) {
279 throw new IllegalStateException(String.format("Trying to override Metadata state for revision %d (known offset: %d)", revision, i));
280 }
281 offsets.put(revision, NONE);
282 }
283
247 // since this is internal class, callers are supposed to ensure arg correctness (i.e. ask known() before) 284 // since this is internal class, callers are supposed to ensure arg correctness (i.e. ask known() before)
248 int dataOffset(int revision) { 285 int dataOffset(int revision) {
249 return offsets.get(revision); 286 return offsets.get(revision);
250 } 287 }
251 void add(int revision, int dataOffset, Collection<MetadataEntry> e) { 288 void add(int revision, int dataOffset, Collection<MetadataEntry> e) {
289 assert !offsets.containsKey(revision);
252 offsets.put(revision, dataOffset); 290 offsets.put(revision, dataOffset);
253 entries.put(revision, e.toArray(new MetadataEntry[e.size()])); 291 entries.put(revision, e.toArray(new MetadataEntry[e.size()]));
254 } 292 }
255 String find(int revision, String key) { 293 String find(int revision, String key) {
256 for (MetadataEntry me : entries.get(revision)) { 294 for (MetadataEntry me : entries.get(revision)) {