~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/knit.py

Finish adapters for annotated knits to unannotated knits and full texts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
142
142
INDEX_SUFFIX = '.kndx'
143
143
 
144
144
 
 
145
class KnitAdapter(object):
 
146
    """Base class for knit record adaption."""
 
147
 
 
148
    def __init__(self):
 
149
        self._data = _KnitData(None)
 
150
        self._annotate_factory = KnitAnnotateFactory()
 
151
        self._plain_factory = KnitPlainFactory()
 
152
 
 
153
 
 
154
class FTAnnotatedToUnannotated(KnitAdapter):
 
155
    """An adapter from FT annotated knits to unannotated ones."""
 
156
 
 
157
    def get_bytes(self, factory, annotated_compressed_bytes):
 
158
        rec, contents = \
 
159
            self._data._parse_record_unchecked(annotated_compressed_bytes)
 
160
        content = self._annotate_factory.parse_fulltext(contents, rec[1])
 
161
        size, bytes = self._data._record_to_data(rec[1], rec[3], content.text())
 
162
        return bytes
 
163
 
 
164
 
 
165
class DeltaAnnotatedToUnannotated(KnitAdapter):
 
166
    """An adapter for deltas from annotated to unannotated."""
 
167
 
 
168
    def get_bytes(self, factory, annotated_compressed_bytes):
 
169
        rec, contents = \
 
170
            self._data._parse_record_unchecked(annotated_compressed_bytes)
 
171
        delta = self._annotate_factory.parse_line_delta(contents, rec[1],
 
172
            plain=True)
 
173
        contents = self._plain_factory.lower_line_delta(delta)
 
174
        size, bytes = self._data._record_to_data(rec[1], rec[3], contents)
 
175
        return bytes
 
176
 
 
177
 
 
178
class FTAnnotatedToFullText(KnitAdapter):
 
179
    """An adapter from FT annotated knits to unannotated ones."""
 
180
 
 
181
    def get_bytes(self, factory, annotated_compressed_bytes):
 
182
        rec, contents = \
 
183
            self._data._parse_record_unchecked(annotated_compressed_bytes)
 
184
        content, delta = self._annotate_factory.parse_record(factory.key[0],
 
185
            contents, factory._build_details, None)
 
186
        return ''.join(content.text())
 
187
 
 
188
 
 
189
class DeltaAnnotatedToFullText(KnitAdapter):
 
190
    """An adapter for deltas from annotated to unannotated."""
 
191
 
 
192
    def __init__(self, basis_vf):
 
193
        """Create an adapter which accesses full texts from basis_vf.
 
194
        
 
195
        :param basis_vf: A versioned file to access basis texts of deltas from.
 
196
        """
 
197
        KnitAdapter.__init__(self)
 
198
        self._basis_vf = basis_vf
 
199
 
 
200
    def get_bytes(self, factory, annotated_compressed_bytes):
 
201
        rec, contents = \
 
202
            self._data._parse_record_unchecked(annotated_compressed_bytes)
 
203
        delta = self._annotate_factory.parse_line_delta(contents, rec[1],
 
204
            plain=True)
 
205
        compression_parent = factory.parents[0][0]
 
206
        basis_lines = self._basis_vf.get_lines(compression_parent)
 
207
        # Manually apply the delta because we have one annotated content and
 
208
        # one plain.
 
209
        basis_content = PlainKnitContent(basis_lines, compression_parent)
 
210
        basis_content.apply_delta(delta, rec[1])
 
211
        basis_content._should_strip_eol = factory._build_details[1]
 
212
        return ''.join(basis_content.text())
 
213
 
 
214
 
145
215
class KnitContentFactory(ContentFactory):
146
216
    """Content factory for streaming from knits.
147
217
    
280
350
                % (e,))
281
351
 
282
352
        if self._should_strip_eol:
283
 
            anno, line = lines[-1]
284
 
            lines[-1] = (anno, line.rstrip('\n'))
 
353
            lines[-1] = lines[-1].rstrip('\n')
285
354
        return lines
286
355
 
287
356
    def copy(self):
2518
2587
                              % (version_id, e.__class__.__name__, str(e)))
2519
2588
        return df, rec
2520
2589
 
2521
 
    def _check_header(self, version_id, line):
 
2590
    def _split_header(self, line):
2522
2591
        rec = line.split()
2523
2592
        if len(rec) != 4:
2524
2593
            raise KnitCorrupt(self._access,
2525
2594
                              'unexpected number of elements in record header')
 
2595
        return rec
 
2596
 
 
2597
    def _check_header_version(self, rec, version_id):
2526
2598
        if rec[1] != version_id:
2527
2599
            raise KnitCorrupt(self._access,
2528
2600
                              'unexpected version, wanted %r, got %r'
2529
2601
                              % (version_id, rec[1]))
 
2602
 
 
2603
    def _check_header(self, version_id, line):
 
2604
        rec = self._split_header(line)
 
2605
        self._check_header_version(rec, version_id)
2530
2606
        return rec
2531
2607
 
2532
 
    def _parse_record(self, version_id, data):
 
2608
    def _parse_record_unchecked(self, data):
2533
2609
        # profiling notes:
2534
2610
        # 4168 calls in 2880 217 internal
2535
2611
        # 4168 calls to _parse_record_header in 2121
2536
2612
        # 4168 calls to readlines in 330
2537
2613
        df = GzipFile(mode='rb', fileobj=StringIO(data))
2538
 
 
2539
2614
        try:
2540
2615
            record_contents = df.readlines()
2541
2616
        except Exception, e:
2542
 
            raise KnitCorrupt(self._access,
2543
 
                              "While reading {%s} got %s(%s)"
2544
 
                              % (version_id, e.__class__.__name__, str(e)))
 
2617
            raise KnitCorrupt(self._access, "Corrupt compressed record %r, got %s(%s)" %
 
2618
                (data, e.__class__.__name__, str(e)))
2545
2619
        header = record_contents.pop(0)
2546
 
        rec = self._check_header(version_id, header)
2547
 
 
 
2620
        rec = self._split_header(header)
2548
2621
        last_line = record_contents.pop()
2549
2622
        if len(record_contents) != int(rec[2]):
2550
2623
            raise KnitCorrupt(self._access,
2551
2624
                              'incorrect number of lines %s != %s'
2552
2625
                              ' for version {%s}'
2553
2626
                              % (len(record_contents), int(rec[2]),
2554
 
                                 version_id))
 
2627
                                 rec[1]))
2555
2628
        if last_line != 'end %s\n' % rec[1]:
2556
2629
            raise KnitCorrupt(self._access,
2557
2630
                              'unexpected version end line %r, wanted %r' 
2558
 
                              % (last_line, version_id))
 
2631
                              % (last_line, rec[1]))
2559
2632
        df.close()
 
2633
        return rec, record_contents
 
2634
 
 
2635
    def _parse_record(self, version_id, data):
 
2636
        rec, record_contents = self._parse_record_unchecked(data)
 
2637
        self._check_header_version(rec, version_id)
2560
2638
        return record_contents, rec[3]
2561
2639
 
2562
2640
    def read_records_iter_raw(self, records):