예제 #1
0
    def _print_omitted_bits(self, tell):
        """
        Display the number of bits omitted since the last displayed value up
        until 'tell'.
        """
        offset = bitstream.to_bit_offset(*tell)
        last_displayed_offset = bitstream.to_bit_offset(*self._last_displayed_tell)
        num_bits = offset - last_displayed_offset

        print(format_omission_line(last_displayed_offset, num_bits))
예제 #2
0
    def _print_error(self, message):
        """
        Print an error message to stderr.
        """
        # Avoid interleaving with stdout (and make causality clearer)
        sys.stdout.flush()

        # The 'trailing bits' print out below might move the read position
        # so record it now.
        if self._reader is not None:
            offset = bitstream.to_bit_offset(*self._reader.tell())

        # Display the trailing bits from the bitstream
        if self._verbose >= 1:
            if self._reader is not None:
                self._reader.seek(*self._last_tell)
                bits = self._reader.try_read_bitarray(self._num_trailing_bits_on_error)
                if len(bits) > 0:
                    sys.stderr.write(
                        "{}\n".format(
                            format_value_line(
                                bitstream.to_bit_offset(*self._last_tell),
                                bitarray(bits),
                                "<next {} bits>".format(len(bits)),
                            )
                        )
                    )

        # Display the traceback
        if self._verbose >= 2:
            if sys.exc_info()[0] is not None:
                traceback.print_exc()

        # Display offset/target information
        prog = os.path.basename(sys.argv[0])
        if self._verbose >= 1:
            if self._reader is not None:
                sys.stderr.write(
                    "{}: offset: {}\n".format(
                        prog,
                        offset,
                    )
                )
            if self._serdes is not None:
                sys.stderr.write(
                    "{}: target: {}\n".format(
                        prog,
                        format_path_summary(self._serdes.path()),
                    )
                )

        # Display the message
        message = "{}: error: {}".format(prog, message)
        sys.stderr.write("{}\n".format(message))
예제 #3
0
 def monitor(des, target, _value):
     # Dirty: Relies on implementation detail of vc2_fixeddicts...
     if des.path(target)[4:] in (
         ["picture_parse", "wavelet_transform", "padding"],
         ["fragment_parse", "fragment_header", "fragment_slice_count"],
         ["fragment_parse", "fragment_header", "fragment_y_offset"],
     ):
         slice_start_offset[0] = to_bit_offset(*des.io.tell())
     elif target == "qindex":
         contained_slices[0] = True
     elif target == "parse_code" and slice_start_offset[0] is not None:
         if contained_slices[0]:
             out.append(to_bit_offset(*des.io.tell()) - slice_start_offset[0])
         contained_slices[0] = False
예제 #4
0
    def _update_status_line(self, target):
        """
        Update the status line, if a sufficiently long time has ellpased (to
        avoid spending too long printing to the console).
        """
        now = time.time()
        if now - self._status_line_updated >= STATUS_LINE_UPDATE_INTERVAL:
            self._status_line_visible = True
            self._status_line_updated = now

            cur_offset = bitstream.to_bit_offset(*self._reader.tell())
            cur_path = format_path_summary(self._serdes.path(target))

            terminal_width = get_terminal_size()[0]
            line = "{:0{}d}: <{}>".format(
                cur_offset,
                OFFSET_DIGITS,
                ellipsise_lossy(cur_path, terminal_width - OFFSET_DIGITS - 4),
            )

            # Ensure stdout is fully displayed before printing the status line
            sys.stdout.flush()

            sys.stderr.write(
                (
                    "\033[2K"  # Clear to end of line
                    "\033[s"  # Save cursor position
                    "{}"
                    "\033[u"  # Restore cursor position
                ).format(line)
            )
            sys.stderr.flush()
예제 #5
0
    def _print_conformance_error(self, exception, tb):
        """
        Display detailed information from a ConformanceError on stdout.
        """
        terminal_width = get_terminal_size()[0]

        summary, _, details = wrap_paragraphs(
            exception.explain()).partition("\n")

        offending_offset = exception.offending_offset()
        if offending_offset is None:
            offending_offset = to_bit_offset(*tell(self._state))

        title = "Conformance error at bit offset {}".format(offending_offset)

        bitstream_viewer_hint = (dedent(
            exception.bitstream_viewer_hint()).strip().format(
                cmd="vc2-bitstream-viewer",
                file=quote(self._filename),
                offset=offending_offset,
            ))

        out = ""

        out += title + "\n"
        out += ("=" * len(title)) + "\n"
        out += "\n"
        out += wrap_paragraphs(summary, terminal_width) + "\n"
        out += "\n"
        out += "\n"
        out += "Details\n"
        out += "-------\n"
        out += "\n"
        out += wrap_paragraphs(details, terminal_width) + "\n"
        out += "\n"
        out += "\n"
        out += "Suggested bitstream viewer commands\n"
        out += "-----------------------------------\n"
        out += "\n"
        out += bitstream_viewer_hint + "\n"
        out += "\n"
        out += "\n"
        out += "Pseudocode traceback\n"
        out += "--------------------\n"
        out += "\n"
        out += "Most recent call last:\n"
        out += "\n"
        out += format_pseudocode_traceback(tb) + "\n"

        print(out)
def test_generate_exp_golomb_numbers_with_ascending_lengths(sign):
    for length, number in islice(
            generate_exp_golomb_with_ascending_lengths(sign),
            128,
    ):
        # Use a known-good implementation of a signed exp-golmb encoder and
        # check length is correct.
        f = BytesIO()
        w = BitstreamWriter(f)
        w.write_sint(number)
        actual_length = to_bit_offset(*w.tell())
        assert actual_length == length

        # Check sign of number
        if sign < 0:
            assert number <= 0
        else:
            assert number >= 0
예제 #7
0
def test_to_and_from_bit_offset(bytes, bits, offset):
    assert bitstream.to_bit_offset(bytes, bits) == offset
    assert bitstream.from_bit_offset(offset) == (bytes, bits)
예제 #8
0
    def __call__(self, serdes, target, value):
        """
        Called as the monitoring function of the the
        :py:class:`MonitoredDeserialiser`. Responsible for printing values from
        the bitstream and terminating once the region of interest has been
        parsed.
        """
        last_tell = self._last_tell
        this_tell = self._reader.tell()
        self._last_tell = this_tell

        if self._show_internal_state:
            # Only show at start of new data unit (NB: strictly speaking this
            # will display the state was it was after parsing the first field
            # of each data unit. Since this is a padding field, the state
            # should still be correct.
            num_data_units = sum(
                len(sequence["data_units"])
                for sequence in self._serdes.context["sequences"]
            )
            if self._last_num_data_units != num_data_units:
                self._last_num_data_units = num_data_units

                self._hide_status_line()
                self._print_internal_state()

        this_offset = bitstream.to_bit_offset(*this_tell)

        enable_display = False
        enable_display = (
            # Within the specified region (NB: we terminate once past it)
            (self._from_offset == 0 or this_offset > self._from_offset)
            and
            # Is the current context selected to be shown
            (
                self._hidden_types is None
                or self._serdes.cur_context.__class__ not in self._hidden_types
            )
            and (
                self._shown_types is None
                or self._serdes.cur_context.__class__ in self._shown_types
            )
        )

        if enable_display:
            self._hide_status_line()

            last_offset = bitstream.to_bit_offset(*last_tell)

            # Print message if we've skipped some bits
            if self._last_displayed_tell != last_tell:
                self._print_omitted_bits(last_tell)
            self._last_displayed_tell = this_tell

            # Re-read the bits within the value just read and display it
            self._reader.seek(*last_tell)
            raw_bits = self._reader.read_bitarray(this_offset - last_offset)
            self._print_value(last_offset, raw_bits, target, value)
        else:
            if self._show_status:
                self._update_status_line(target)

        # Stop if we've encountered an invalid parse_info prefix code
        if self._check_parse_info_prefix:
            # The test below relies on the only use of the target name
            # 'parse_info_prefix' being for the parse info prefix. (This should
            # be the case).
            if target == "parse_info_prefix" and value != PARSE_INFO_PREFIX:
                raise BitstreamViewer._TerminateError(
                    "invalid parse_info prefix ({})".format(Hex(8)(value))
                )

        # Stop when we've reached/passed the end of the region of interest
        if self._to_offset != 0 and this_offset >= self._to_offset:
            raise BitstreamViewer._TerminateSuccess()

        # Save memory by discarding previously deserialised data units
        current_data_unit = True
        for sequence in reversed(self._serdes.context["sequences"]):
            for i in reversed(range(len(sequence["data_units"]))):
                if not current_data_unit:
                    sequence["data_units"][i] = None
                current_data_unit = False
    def test_happy_cases(self, block_bits, num_values, magnitude):
        value_sets = {
            dangle_type: generate_dangling_transform_values(
                block_bits,
                num_values,
                dangle_type,
                magnitude,
            )
            for dangle_type in DanglingTransformValueType
        }

        values_and_bits_beyond_ends = {}
        for description, values in value_sets.items():
            # Should all have required number of values
            assert len(values) == num_values

            # Should correctly encode into bounded block
            f = BytesIO()
            w = BitstreamWriter(f)
            w.bounded_block_begin(block_bits)
            for value in values:
                w.write_sint(value)

            # Should completely fill the block
            length_used = to_bit_offset(*w.tell())
            assert length_used == block_bits

            # Check we actually wrote 'beyond' the end of the block
            assert w.bits_remaining < 0

            # Work out which value and which bits actually first crossed the
            # end-of-block boundary (we'll later check that these actually
            # match our expectations later)
            w.flush()
            f.seek(0)
            r = BitstreamReader(f)
            r.bounded_block_begin(block_bits)
            value_beyond_end = None
            bits_beyond_end = None
            while r.bits_remaining >= 0:
                value_beyond_end = r.read_sint()
                bits_beyond_end = -r.bits_remaining
            values_and_bits_beyond_ends[description] = (
                value_beyond_end,
                bits_beyond_end,
            )

        # Check that the dangling value dangles in the expected way
        v, b = values_and_bits_beyond_ends[
            DanglingTransformValueType.zero_dangling]
        assert v == 0
        assert b == 1

        v, b = values_and_bits_beyond_ends[
            DanglingTransformValueType.sign_dangling]
        assert v != 0
        assert (-v).bit_length() == magnitude
        assert b == 1

        v, b = values_and_bits_beyond_ends[
            DanglingTransformValueType.stop_and_sign_dangling]
        assert v != 0
        assert (-v).bit_length() == magnitude
        assert b == 2

        v, b = values_and_bits_beyond_ends[
            DanglingTransformValueType.lsb_stop_and_sign_dangling]
        assert v != 0
        # NB: Larger due to exp-golmb code needing to end in 1
        assert (-v).bit_length() == magnitude + 1
        assert b == 3