1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.treetank.access;
29
30 import static com.google.common.base.Objects.toStringHelper;
31 import static com.google.common.base.Preconditions.checkNotNull;
32 import static com.google.common.base.Preconditions.checkState;
33 import static org.treetank.access.BucketReadTrx.dataBucketOffset;
34
35 import java.io.File;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Set;
41 import java.util.Stack;
42 import java.util.concurrent.Callable;
43 import java.util.concurrent.ExecutorService;
44 import java.util.concurrent.Executors;
45
46 import org.jclouds.javax.annotation.Nullable;
47 import org.treetank.access.conf.ConstructorProps;
48 import org.treetank.api.IBucketWriteTrx;
49 import org.treetank.api.IMetaEntry;
50 import org.treetank.api.IData;
51 import org.treetank.api.ISession;
52 import org.treetank.bucket.BucketFactory;
53 import org.treetank.bucket.IConstants;
54 import org.treetank.bucket.IndirectBucket;
55 import org.treetank.bucket.MetaBucket;
56 import org.treetank.bucket.DataBucket;
57 import org.treetank.bucket.DataBucket.DeletedData;
58 import org.treetank.bucket.RevisionRootBucket;
59 import org.treetank.bucket.UberBucket;
60 import org.treetank.bucket.interfaces.IBucket;
61 import org.treetank.bucket.interfaces.IReferenceBucket;
62 import org.treetank.exception.TTException;
63 import org.treetank.exception.TTIOException;
64 import org.treetank.io.IBackendWriter;
65 import org.treetank.io.ILog;
66 import org.treetank.io.LRULog;
67 import org.treetank.io.LogKey;
68 import org.treetank.io.LogValue;
69
70 import com.google.common.cache.Cache;
71 import com.google.common.cache.CacheBuilder;
72 import com.google.common.io.ByteArrayDataInput;
73 import com.google.common.io.ByteArrayDataOutput;
74 import com.google.common.io.ByteStreams;
75
76
77
78
79
80
81
82
83 public final class BucketWriteTrx implements IBucketWriteTrx {
84
85
86 private final IBackendWriter mBackendWriter;
87
88
89 private UberBucket mNewUber;
90
91
92 private RevisionRootBucket mNewRoot;
93
94
95 private MetaBucket mNewMeta;
96
97
98 private BucketReadTrx mDelegate;
99
100
101 private final ExecutorService mCommitInProgress;
102
103
104 private final BucketFactory mBucketFac;
105
106
107 private ILog mLog;
108
109
110 @Nullable
111 private ILog mFormerLog;
112
113
114 private final Cache<Long, byte[]> mFormerDataBucketHashes;
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 protected BucketWriteTrx(final ISession pSession, final UberBucket pUberBucket,
132 final IBackendWriter pWriter, final long pRepresentRev) throws TTException {
133 mBackendWriter = pWriter;
134
135 final long revkey =
136 BucketReadTrx.dereferenceLeafOfTree(pWriter,
137 pUberBucket.getReferenceKeys()[IReferenceBucket.GUARANTEED_INDIRECT_OFFSET], pRepresentRev)[IConstants.INDIRECT_BUCKET_COUNT.length];
138 final RevisionRootBucket revBucket = (RevisionRootBucket)pWriter.read(revkey);
139 final MetaBucket metaBucket =
140 (MetaBucket)pWriter.read(revBucket.getReferenceKeys()[RevisionRootBucket.META_REFERENCE_OFFSET]);
141
142 mCommitInProgress = Executors.newSingleThreadExecutor();
143 mDelegate = new BucketReadTrx(pSession, pUberBucket, revBucket, metaBucket, pWriter);
144 mBucketFac = new BucketFactory(pSession.getConfig().mDataFac, pSession.getConfig().mMetaFac);
145
146
147 mLog =
148 new LRULog(new File(pSession.getConfig().mProperties
149 .getProperty(org.treetank.access.conf.ConstructorProps.RESOURCEPATH)),
150 pSession.getConfig().mDataFac, pSession.getConfig().mMetaFac);
151
152 mFormerLog = mLog;
153 mFormerDataBucketHashes = CacheBuilder.newBuilder().maximumSize(16384).build();
154 setUpTransaction(pUberBucket, revBucket, metaBucket, pSession, pRepresentRev);
155 }
156
157
158
159
160 public long setData(final IData pData) throws TTException {
161
162 checkState(!mDelegate.isClosed(), "Transaction already closed");
163
164 final long dataKey = pData.getDataKey();
165 final long seqBucketKey = dataKey >> IConstants.INDIRECT_BUCKET_COUNT[3];
166 final int dataBucketOffset = dataBucketOffset(dataKey);
167 final LogValue container = prepareDataBucket(dataKey);
168 final DataBucket modified = ((DataBucket)container.getModified());
169 final DataBucket complete = ((DataBucket)container.getComplete());
170 modified.setData(dataBucketOffset, pData);
171 complete.setData(dataBucketOffset, pData);
172 mLog.put(new LogKey(false, IConstants.INDIRECT_BUCKET_COUNT.length, seqBucketKey), container);
173 return dataKey;
174 }
175
176
177
178
179 @Override
180 public void removeData(final IData pData) throws TTException {
181 checkState(!mDelegate.isClosed(), "Transaction already closed");
182 checkNotNull(pData);
183 final long dataBucketKey = pData.getDataKey() >> IConstants.INDIRECT_BUCKET_COUNT[3];
184 LogValue container = prepareDataBucket(pData.getDataKey());
185 final IData delData = new DeletedData(pData.getDataKey());
186 ((DataBucket)container.getComplete()).setData(dataBucketOffset(pData.getDataKey()), delData);
187 ((DataBucket)container.getModified()).setData(dataBucketOffset(pData.getDataKey()), delData);
188
189 mLog.put(new LogKey(false, IConstants.INDIRECT_BUCKET_COUNT.length, dataBucketKey), container);
190 }
191
192
193
194
195 public IData getData(final long pDataKey) throws TTIOException {
196 checkState(!mDelegate.isClosed(), "Transaction already closed");
197
198 final long dataBucketKey = pDataKey >> IConstants.INDIRECT_BUCKET_COUNT[3];
199 final int dataBucketOffset = dataBucketOffset(pDataKey);
200
201 final LogKey key = new LogKey(false, IConstants.INDIRECT_BUCKET_COUNT.length, dataBucketKey);
202 LogValue container = mLog.get(key);
203 IData item = null;
204
205 if (container.getModified() != null) {
206
207 if (((DataBucket)container.getModified()).getData(dataBucketOffset) == null) {
208 item = ((DataBucket)container.getComplete()).getData(dataBucketOffset);
209 }
210 else {
211 item = ((DataBucket)container.getModified()).getData(dataBucketOffset);
212 }
213 checkNotNull(item, "Item must be set!");
214 item = mDelegate.checkItemIfDeleted(item);
215 }
216 else {
217
218 container = mFormerLog.get(key);
219
220 if (container.getModified() != null) {
221
222 if (((DataBucket)container.getModified()).getData(dataBucketOffset) == null) {
223 item = clone(((DataBucket)container.getComplete())).getData(dataBucketOffset);
224 }
225 else {
226 item = clone(((DataBucket)container.getComplete())).getData(dataBucketOffset);
227 }
228 checkNotNull(item, "Item must be set!");
229 item = mDelegate.checkItemIfDeleted(item);
230 }
231 else {
232 item = mDelegate.getData(pDataKey);
233 }
234 }
235 return item;
236 }
237
238
239
240
241 @Override
242 public void commit() throws TTException {
243 checkState(!mDelegate.isClosed(), "Transaction already closed");
244
245 mDelegate.mSession.waitForRunningCommit();
246
247 final UberBucket uber = clone(mNewUber);
248 final MetaBucket meta = clone(mNewMeta);
249 final RevisionRootBucket rev = clone(mNewRoot);
250
251 mFormerLog = mLog;
252
253
254 mLog =
255 new LRULog(new File(mDelegate.mSession.getConfig().mProperties
256 .getProperty(org.treetank.access.conf.ConstructorProps.RESOURCEPATH)), mDelegate.mSession
257 .getConfig().mDataFac, mDelegate.mSession.getConfig().mMetaFac);
258
259 mDelegate.mSession.setRunningCommit(mCommitInProgress.submit(new CommitCallable(uber, rev, meta)));
260
261
262
263 setUpTransaction(uber, rev, meta, mDelegate.mSession, uber.getRevisionNumber());
264
265 }
266
267
268
269
270 @Override
271 public void commitBlocked() throws TTException {
272 commit();
273 mDelegate.mSession.waitForRunningCommit();
274 }
275
276 public void clearLog() throws TTIOException {
277 mLog.close();
278 }
279
280
281
282
283 @Override
284 public boolean close() throws TTIOException {
285 mCommitInProgress.shutdown();
286 mDelegate.mSession.waitForRunningCommit();
287 if (!mDelegate.isClosed()) {
288 mDelegate.close();
289
290 try {
291
292
293
294 mLog.close();
295 mBackendWriter.close();
296 } catch (IllegalStateException e) {
297
298 }
299 mDelegate.mSession.deregisterBucketTrx(this);
300 return true;
301 } else {
302 return false;
303 }
304 }
305
306
307
308
309 @Override
310 public long incrementDataKey() {
311 checkState(!mDelegate.isClosed(), "Transaction already closed");
312 return mNewRoot.incrementMaxDataKey();
313 }
314
315
316
317
318 @Override
319 public long getRevision() throws TTIOException {
320 checkState(!mDelegate.isClosed(), "Transaction already closed");
321 return mNewRoot.getRevision();
322 }
323
324
325
326
327 @Override
328 public boolean isClosed() {
329 return mDelegate.isClosed();
330 }
331
332
333
334
335 @Override
336 public MetaBucket getMetaBucket() {
337 checkState(!mDelegate.isClosed(), "Transaction already closed");
338 return mNewMeta;
339 }
340
341 private LogValue prepareDataBucket(final long pDataKey) throws TTException {
342
343 final long seqDataBucketKey = pDataKey >> IConstants.INDIRECT_BUCKET_COUNT[3];
344
345 final LogKey key = new LogKey(false, IConstants.INDIRECT_BUCKET_COUNT.length, seqDataBucketKey);
346
347 LogValue container = mLog.get(key);
348
349 if (container.getModified() == null) {
350
351 final LogKey indirectKey = preparePathToLeaf(false, mNewRoot, pDataKey);
352 final LogValue indirectContainer = mLog.get(indirectKey);
353 final int dataOffset = dataBucketOffset(seqDataBucketKey);
354 final long bucketKey =
355 ((IndirectBucket)indirectContainer.getModified()).getReferenceKeys()[dataOffset];
356 final long newBucketKey = mNewUber.incrementBucketCounter();
357
358 if (bucketKey != 0) {
359
360 final int revToRestore =
361 Integer.parseInt(mDelegate.mSession.getConfig().mProperties
362 .getProperty(ConstructorProps.NUMBERTORESTORE));
363
364
365 final LogValue formerModified = mFormerLog.get(key);
366
367 final List<DataBucket> formerBuckets = mDelegate.getSnapshotBuckets(seqDataBucketKey);
368
369 final List<DataBucket> bucketList = new ArrayList<DataBucket>();
370
371
372 if (formerModified.getModified() != null) {
373
374 final DataBucket currentlyInProgress = (DataBucket)formerModified.getModified();
375
376 if (formerBuckets.isEmpty()
377 || formerBuckets.get(0).getBucketKey() < currentlyInProgress.getBucketKey()) {
378 bucketList.add((DataBucket)clone(currentlyInProgress));
379 }
380 }
381
382
383 if (bucketList.isEmpty() || formerBuckets.size() < revToRestore) {
384
385 bucketList.addAll(formerBuckets);
386 }
387 else {
388 if (formerBuckets.size() > 1) {
389 bucketList.addAll(formerBuckets.subList(0, formerBuckets.size() - 1));
390 }
391 }
392
393
394 final DataBucket[] buckets = bucketList.toArray(new DataBucket[bucketList.size()]);
395
396 checkState(buckets.length > 0);
397 container =
398 mDelegate.mSession.getConfig().mRevision.combineBucketsForModification(revToRestore,
399 newBucketKey, buckets, mNewRoot.getRevision() % revToRestore == 0);
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421 }
422 else {
423 final DataBucket newBucket = new DataBucket(newBucketKey, IConstants.NULLDATA);
424 container = new LogValue(newBucket, newBucket);
425 }
426 ((IndirectBucket)indirectContainer.getModified()).setReferenceKey(dataOffset, newBucketKey);
427 ((IndirectBucket)indirectContainer.getModified()).setReferenceHash(dataOffset,
428 IConstants.NON_HASHED);
429 mLog.put(indirectKey, indirectContainer);
430 mLog.put(key, container);
431 }
432 return container;
433 }
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450 private LogKey preparePathToLeaf(final boolean pIsRootLevel, final IReferenceBucket pBucket,
451 final long pElementKey) throws TTException {
452
453
454
455
456
457 long seqBucketKey = -1;
458
459
460 if (pIsRootLevel) {
461 seqBucketKey = pElementKey >> IConstants.INDIRECT_BUCKET_COUNT[3];
462 }
463
464 else {
465 seqBucketKey = pElementKey >> IConstants.INDIRECT_BUCKET_COUNT[2];
466 }
467
468 long[] orderNumber = new long[IConstants.INDIRECT_BUCKET_COUNT.length];
469 for (int level = 0; level < orderNumber.length; level++) {
470 orderNumber[level] = seqBucketKey >> IConstants.INDIRECT_BUCKET_COUNT[level];
471 }
472
473 IReferenceBucket bucket = null;
474 IReferenceBucket parentBucket = pBucket;
475 LogKey key = null;
476 LogKey parentKey = new LogKey(pIsRootLevel, -1, 0);
477
478
479 for (int level = 0; level < orderNumber.length; level++) {
480
481 key = new LogKey(pIsRootLevel, level, orderNumber[level]);
482 LogValue container = mLog.get(key);
483
484 if (container.getModified() == null) {
485
486 final long newKey = mNewUber.incrementBucketCounter();
487 bucket = new IndirectBucket(newKey);
488
489
490 int offset = dataBucketOffset(orderNumber[level]);
491
492
493 container = mFormerLog.get(key);
494 IReferenceBucket oldBucket = null;
495 if (container.getModified() != null) {
496 oldBucket = (IReferenceBucket)container.getModified();
497 }
498
499
500 else if (parentBucket.getReferenceKeys()[offset] != 0) {
501 oldBucket =
502 (IReferenceBucket)mBackendWriter.read(parentBucket.getReferenceKeys()[offset]);
503 }
504
505 if (oldBucket != null) {
506 for (int i = 0; i < oldBucket.getReferenceKeys().length; i++) {
507 bucket.setReferenceKey(i, oldBucket.getReferenceKeys()[i]);
508 bucket.setReferenceHash(i, oldBucket.getReferenceHashs()[i]);
509 }
510 }
511
512
513 parentBucket.setReferenceKey(offset, newKey);
514
515 parentBucket.setReferenceHash(offset, IConstants.NON_HASHED);
516
517 container = new LogValue(parentBucket, parentBucket);
518
519
520 if (level > 0) {
521 mLog.put(parentKey, container);
522 }
523
524 container = new LogValue(bucket, bucket);
525 mLog.put(key, container);
526
527 }
528 else {
529 bucket = (IReferenceBucket)container.getModified();
530 }
531
532 parentKey = key;
533 parentBucket = bucket;
534 }
535
536
537 return key;
538 }
539
540 private void setUpTransaction(final UberBucket pUberOld, final RevisionRootBucket pRootToRepresent,
541 final MetaBucket pMetaOld, final ISession pSession, final long pRepresentRev) throws TTException {
542
543 mNewUber =
544 new UberBucket(pUberOld.getBucketCounter() + 1, pUberOld.getRevisionNumber() + 1, pUberOld
545 .getBucketCounter() + 1);
546 mNewUber.setReferenceKey(IReferenceBucket.GUARANTEED_INDIRECT_OFFSET,
547 pUberOld.getReferenceKeys()[IReferenceBucket.GUARANTEED_INDIRECT_OFFSET]);
548 mNewUber.setReferenceHash(IReferenceBucket.GUARANTEED_INDIRECT_OFFSET, IConstants.NON_HASHED);
549
550
551
552 final LogKey indirectKey = preparePathToLeaf(true, mNewUber, mNewUber.getRevisionNumber());
553 final LogValue indirectContainer = mLog.get(indirectKey);
554 final int offset = dataBucketOffset(mNewUber.getRevisionNumber());
555
556
557
558 mNewRoot =
559 new RevisionRootBucket(mNewUber.incrementBucketCounter(), pRepresentRev + 1, pRootToRepresent
560 .getMaxDataKey());
561 mNewRoot.setReferenceKey(IReferenceBucket.GUARANTEED_INDIRECT_OFFSET, pRootToRepresent
562 .getReferenceKeys()[IReferenceBucket.GUARANTEED_INDIRECT_OFFSET]);
563 mNewRoot.setReferenceHash(IReferenceBucket.GUARANTEED_INDIRECT_OFFSET, IConstants.NON_HASHED);
564
565
566 ((IndirectBucket)indirectContainer.getModified()).setReferenceKey(offset, mNewRoot.getBucketKey());
567 ((IndirectBucket)indirectContainer.getModified()).setReferenceHash(offset, IConstants.NON_HASHED);
568
569 mLog.put(indirectKey, indirectContainer);
570
571
572 final Set<Map.Entry<IMetaEntry, IMetaEntry>> keySet = pMetaOld.entrySet();
573 mNewMeta = new MetaBucket(mNewUber.incrementBucketCounter());
574 for (final Map.Entry<IMetaEntry, IMetaEntry> key : keySet) {
575 mNewMeta.put(clone(key.getKey()), clone(key.getValue()));
576 }
577 mNewRoot.setReferenceKey(RevisionRootBucket.META_REFERENCE_OFFSET, mNewMeta.getBucketKey());
578 mNewRoot.setReferenceHash(RevisionRootBucket.META_REFERENCE_OFFSET, IConstants.NON_HASHED);
579
580 }
581
582
583
584
585 @Override
586 public String toString() {
587 return toStringHelper(this).add("mDelegate", mDelegate).add("mBackendWriter", mBackendWriter).add(
588 "mRootBucket", mNewRoot).add("mDelegate", mDelegate).toString();
589 }
590
591 private IMetaEntry clone(final IMetaEntry pToClone) throws TTIOException {
592 final ByteArrayDataOutput output = ByteStreams.newDataOutput();
593 pToClone.serialize(output);
594 final ByteArrayDataInput input = ByteStreams.newDataInput(output.toByteArray());
595 return mDelegate.mSession.getConfig().mMetaFac.deserializeEntry(input);
596 }
597
598 @SuppressWarnings("unchecked")
599 private <E extends IBucket> E clone(final E pToClone) throws TTIOException {
600 final ByteArrayDataOutput output = ByteStreams.newDataOutput();
601 pToClone.serialize(output);
602 final ByteArrayDataInput input = ByteStreams.newDataInput(output.toByteArray());
603 return (E)mBucketFac.deserializeBucket(input);
604 }
605
606
607
608
609
610
611
612 private void closeFormerLog() throws TTIOException {
613 if (mFormerLog != null && !mFormerLog.isClosed()) {
614 mFormerLog.close();
615 }
616 }
617
618
619
620
621
622
623
624 class CommitCallable implements Callable<Void> {
625
626 final MetaBucket mMeta;
627 final RevisionRootBucket mRoot;
628 final UberBucket mUber;
629
630
631
632
633
634
635
636
637
638
639
640 CommitCallable(final UberBucket pUber, final RevisionRootBucket pRoot, final MetaBucket pMeta) {
641 mUber = pUber;
642 mRoot = pRoot;
643 mMeta = pMeta;
644 }
645
646
647
648
649 @Override
650 public Void call() throws Exception {
651
652
653 iterateSubtree(false);
654
655 final LogKey dataKey = new LogKey(false, 0, 0);
656 final IReferenceBucket dataBuck = (IReferenceBucket)mFormerLog.get(dataKey).getModified();
657
658 if (dataBuck != null) {
659 final byte[] dataHash = dataBuck.secureHash().asBytes();
660 mBackendWriter.write(dataBuck);
661 mRoot.setReferenceHash(RevisionRootBucket.GUARANTEED_INDIRECT_OFFSET, dataHash);
662 }
663
664 final byte[] metaHash = mMeta.secureHash().asBytes();
665 mBackendWriter.write(mMeta);
666 mRoot.setReferenceHash(RevisionRootBucket.META_REFERENCE_OFFSET, metaHash);
667
668
669 iterateSubtree(true);
670
671 final LogKey revKey = new LogKey(true, 0, 0);
672 final IReferenceBucket revBuck = (IReferenceBucket)mFormerLog.get(revKey).getModified();
673 final byte[] revHash = revBuck.secureHash().asBytes();
674 mBackendWriter.write(revBuck);
675 mUber.setReferenceHash(UberBucket.GUARANTEED_INDIRECT_OFFSET, revHash);
676 mBackendWriter.writeUberBucket(mUber);
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698 ((Session)mDelegate.mSession).setLastCommittedUberBucket(mUber);
699 mDelegate = new BucketReadTrx(mDelegate.mSession, mUber, mRoot, mMeta, mBackendWriter);
700 closeFormerLog();
701
702
703 return null;
704 }
705
706
707
708
709
710
711
712
713 private void iterateSubtree(final boolean pRootLevel) throws TTException {
714 IReferenceBucket currentRefBuck;
715
716 final Stack<LogKey> childAndRightSib = new Stack<LogKey>();
717
718 final Stack<LogKey> pathToRoot = new Stack<LogKey>();
719
720 childAndRightSib.push(new LogKey(pRootLevel, 0, 0));
721
722
723 if (mFormerLog.get(childAndRightSib.peek()).getModified() == null) {
724 return;
725 }
726
727
728 while (!childAndRightSib.isEmpty()) {
729
730 final LogKey key = childAndRightSib.pop();
731 final IBucket val = mFormerLog.get(key).getModified();
732
733
734 if (val instanceof IReferenceBucket) {
735 currentRefBuck = (IReferenceBucket)val;
736
737
738
739 if (pathToRoot.isEmpty() || key.getLevel() > pathToRoot.peek().getLevel()) {
740
741 pathToRoot.push(key);
742 }
743
744 else {
745 LogKey childKey;
746
747 do {
748
749 final LogKey parentKey = pathToRoot.peek();
750 childKey = pathToRoot.pop();
751 adaptHash(parentKey, childKey);
752 }
753 while (childKey.getLevel() > key.getLevel());
754
755 pathToRoot.push(key);
756 }
757
758
759 for (int i = currentRefBuck.getReferenceHashs().length - 1; i >= 0; i--) {
760
761 final byte[] hash = currentRefBuck.getReferenceHashs()[i];
762
763 if (Arrays.equals(hash, IConstants.NON_HASHED)) {
764
765 final LogKey toPush =
766 new LogKey(pRootLevel, key.getLevel() + 1,
767 (key.getSeq() << IConstants.INDIRECT_BUCKET_COUNT[3]) + i);
768 childAndRightSib.push(toPush);
769 }
770 }
771
772 }
773 else {
774
775 if (pRootLevel) {
776 final byte[] hash = mRoot.secureHash().asBytes();
777 mBackendWriter.write(mRoot);
778 final LogKey parentKey = pathToRoot.peek();
779 final IReferenceBucket parentVal =
780 (IReferenceBucket)mFormerLog.get(parentKey).getModified();
781 final int parentOffset =
782 (int)(key.getSeq() - ((key.getSeq() >> IConstants.INDIRECT_BUCKET_COUNT[3]) << IConstants.INDIRECT_BUCKET_COUNT[3]));
783 parentVal.setReferenceHash(parentOffset, hash);
784
785 }
786 else {
787
788
789
790 if (val == null) {
791 final IReferenceBucket parent =
792 (IReferenceBucket)mFormerLog.get(pathToRoot.peek()).getModified();
793 final int parentOffset =
794 (int)(key.getSeq() - ((key.getSeq() >> IConstants.INDIRECT_BUCKET_COUNT[3]) << IConstants.INDIRECT_BUCKET_COUNT[3]));
795 byte[] persistedHash = mFormerDataBucketHashes.getIfPresent(key.getSeq());
796 if (persistedHash == null) {
797 final IBucket persistedBucket =
798 mBackendWriter.read(parent.getReferenceKeys()[parentOffset]);
799 persistedHash = persistedBucket.secureHash().asBytes();
800 mFormerDataBucketHashes.put(key.getSeq(), persistedHash);
801 }
802 parent.setReferenceHash(parentOffset, persistedHash);
803 }
804 else {
805
806 checkState(val instanceof DataBucket);
807
808 final LogKey parentKey = pathToRoot.peek();
809 adaptHash(parentKey, key);
810 }
811 }
812
813 }
814 }
815
816
817
818 if (!pathToRoot.isEmpty()) {
819 do {
820 final LogKey child = pathToRoot.pop();
821 final LogKey parent = pathToRoot.peek();
822 adaptHash(parent, child);
823 } while (pathToRoot.size() > 1);
824 }
825
826 }
827
828
829
830
831
832
833
834
835
836
837 private void adaptHash(final LogKey pParentKey, final LogKey pChildKey) throws TTException {
838 final IBucket val = mFormerLog.get(pChildKey).getModified();
839 final byte[] hash = val.secureHash().asBytes();
840 mBackendWriter.write(val);
841 if (val instanceof DataBucket) {
842 mFormerDataBucketHashes.put(pChildKey.getSeq(), hash);
843 }
844
845 final IReferenceBucket parent = (IReferenceBucket)mFormerLog.get(pParentKey).getModified();
846 final int parentOffset =
847 (int)(pChildKey.getSeq() - ((pChildKey.getSeq() >> IConstants.INDIRECT_BUCKET_COUNT[3]) << IConstants.INDIRECT_BUCKET_COUNT[3]));
848 parent.setReferenceHash(parentOffset, hash);
849 }
850 }
851 }