643
643
raise errors.NoSmartMedium(self)
645
def readv(self, relpath, offsets):
646
"""Get parts of the file at the given relative path.
648
:offsets: A list of (offset, size) tuples.
645
def readv(self, relpath, offsets, adjust_for_latency=False,
647
"""Get parts of the file at the given relative path.
649
:param relpath: The path to read data from.
650
:param offsets: A list of (offset, size) tuples.
651
:param adjust_for_latency: Adjust the requested offsets to accomdate
652
transport latency. This may re-order the offsets, expand them to
653
grab adjacent data when there is likely a high cost to requesting
654
data relative to delivering it.
655
:param upper_limit: When adjust_for_latency is True setting upper_limit
656
allows the caller to tell the transport about the length of the
657
file, so that requests are not issued for ranges beyond the end of
658
the file. This matters because some servers and/or transports error
659
in such a case rather than just satisfying the available ranges.
660
upper_limit should always be provided when adjust_for_latency is
661
True, and should be the size of the file in bytes.
662
:return: A list or generator of (offset, data) tuples
664
if adjust_for_latency:
665
offsets = sorted(offsets)
666
# short circuit empty requests
667
if len(offsets) == 0:
669
# Quick thunk to stop this function becoming a generator
670
# itself, rather we return a generator that has nothing to
674
return empty_yielder()
675
# expand by page size at either end
676
expansion = self.recommended_page_size()
677
reduction = expansion / 2
679
for offset, length in offsets:
680
new_offset = offset - reduction
681
new_length = length + expansion
683
# don't ask for anything < 0
684
new_length -= new_offset
686
if (upper_limit is not None and
687
new_offset + new_length > upper_limit):
688
new_length = upper_limit - new_offset
689
new_offsets.append((new_offset, new_length))
690
# combine the expanded offsets
692
current_offset, current_length = new_offsets[0]
693
current_finish = current_length + current_offset
694
for offset, length in new_offsets[1:]:
695
if offset > current_finish:
696
offsets.append((current_offset, current_length))
697
current_offset = offset
698
current_length = length
700
finish = offset + length
701
if finish > current_finish:
702
current_finish = finish
703
offsets.append((current_offset, current_length))
704
return self._readv(relpath, offsets)
706
def _readv(self, relpath, offsets):
707
"""Get parts of the file at the given relative path.
709
:param relpath: The path to read.
710
:param offsets: A list of (offset, size) tuples.
649
711
:return: A list or generator of (offset, data) tuples
1609
1671
raise NotImplementedError
1612
class TransportLogger(object):
1613
"""Adapt a transport to get clear logging data on api calls.
1615
Feel free to extend to log whatever calls are of interest.
1618
def __init__(self, adapted):
1619
self._adapted = adapted
1622
def get(self, name):
1623
self._calls.append((name,))
1624
return self._adapted.get(name)
1626
def __getattr__(self, name):
1627
"""Thunk all undefined access through to self._adapted."""
1628
# raise AttributeError, name
1629
return getattr(self._adapted, name)
1631
def readv(self, name, offsets):
1632
self._calls.append((name, offsets))
1633
return self._adapted.readv(name, offsets)
1636
1674
# None is the default transport, for things with no url scheme
1637
1675
register_transport_proto('file://',
1638
1676
help="Access using the standard filesystem (default)")
1699
1739
register_transport_proto('fakenfs+')
1700
1740
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')
1742
register_transport_proto('trace+')
1743
register_lazy_transport('trace+', 'bzrlib.transport.trace', 'TransportTraceDecorator')
1702
1745
register_transport_proto('unlistable+')
1703
1746
register_lazy_transport('unlistable+', 'bzrlib.transport.unlistable', 'UnlistableTransportDecorator')