Skip to content

Instantly share code, notes, and snippets.

@Tblue
Last active September 4, 2025 21:07
Show Gist options
  • Save Tblue/62ff47bef7f894e92ed5 to your computer and use it in GitHub Desktop.
Save Tblue/62ff47bef7f894e92ed5 to your computer and use it in GitHub Desktop.

Revisions

  1. Tblue revised this gist Mar 19, 2022. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion mozlz4a.py
    Original file line number Diff line number Diff line change
    @@ -104,7 +104,9 @@ def get_argparser():
    p.add_argument(
    "out_file",
    type=BinFileArg("w"),
    help="Path to output file. `-' means standard output."
    nargs="?",
    default="-",
    help="Path to output file. `-' means standard output (and is the default)."
    )

    return p
  2. Tblue revised this gist Mar 19, 2022. 1 changed file with 91 additions and 52 deletions.
    143 changes: 91 additions & 52 deletions mozlz4a.py
    Original file line number Diff line number Diff line change
    @@ -1,15 +1,28 @@
    #!/usr/bin/env python
    #!/usr/bin/env python3
    # vim: sw=4 ts=4 et tw=100 cc=+1
    #
    ####################################################################################################
    # DESCRIPTION #
    ####################################################################################################
    #
    # Decompressor/compressor for files in Mozilla's "mozLz4" format. Firefox uses this file format to
    # compress e. g. bookmark backups (*.jsonlz4).
    #
    # This file format is in fact just plain LZ4 data with a custom header (magic number [8 bytes] and
    # uncompressed file size [4 bytes, little endian]).
    #
    # This Python 3 script requires the LZ4 bindings for Python, see: https://pypi.python.org/pypi/lz4
    ####################################################################################################
    # DEPENDENCIES #
    ####################################################################################################
    #
    # - Tested with Python 3.10
    # - LZ4 bindings for Python, version 4.x: https://pypi.python.org/pypi/lz4
    #
    ####################################################################################################
    # LICENSE #
    ####################################################################################################
    #
    # Copyright (c) 2015, Tilman Blumenbach
    # Copyright (c) 2015-2022, Tilman Blumenbach
    # All rights reserved.
    #
    # Redistribution and use in source and binary forms, with or without modification, are permitted
    @@ -30,79 +43,105 @@
    # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    import lz4
    import argparse
    import sys

    from argparse import ArgumentParser
    import lz4.block


    class MozLz4aError(Exception):
    pass
    class BinFileArg:
    def __init__(self, mode):
    self._mode = mode

    def __call__(self, arg):
    objs = {
    "r": sys.stdin.buffer,
    "w": sys.stdout.buffer,
    }

    class InvalidHeader(MozLz4aError):
    def __init__(self, msg):
    self.msg = msg
    if arg == "-":
    return objs[self._mode]

    def __str__(self):
    return self.msg
    try:
    return open(arg, self._mode + "b")
    except OSError as e:
    raise argparse.ArgumentTypeError(
    "failed to open file for %s: %s" % (
    "reading" if self._mode == "r" else "writing",
    e
    )
    )


    def decompress(file_obj):
    if file_obj.read(8) != b"mozLz40\0":
    raise InvalidHeader("Invalid magic number")
    raise ValueError("Invalid magic number")

    return lz4.block.decompress(file_obj.read())

    return lz4.decompress(file_obj.read())

    def compress(file_obj):
    compressed = lz4.compress(file_obj.read())
    compressed = lz4.block.compress(file_obj.read())
    return b"mozLz40\0" + compressed


    if __name__ == "__main__":
    argparser = ArgumentParser(description="MozLz4a compression/decompression utility")
    argparser.add_argument(
    "-d", "--decompress", "--uncompress",
    action="store_true",
    help="Decompress the input file instead of compressing it."
    )
    argparser.add_argument(
    "in_file",
    help="Path to input file."
    )
    argparser.add_argument(
    "out_file",
    help="Path to output file."
    )
    def get_argparser():
    p = argparse.ArgumentParser(
    description="MozLz4a compression/decompression utility"
    )

    parsed_args = argparser.parse_args()
    p.add_argument(
    "-d", "--decompress", "--uncompress",
    action="store_true",
    help="Decompress the input file instead of compressing it."
    )

    p.add_argument(
    "in_file",
    type=BinFileArg("r"),
    help="Path to input file. `-' means standard input."
    )
    p.add_argument(
    "out_file",
    type=BinFileArg("w"),
    help="Path to output file. `-' means standard output."
    )

    try:
    in_file = open(parsed_args.in_file, "rb")
    except IOError as e:
    print("Could not open input file `%s' for reading: %s" % (parsed_args.in_file, e), file=sys.stderr)
    sys.exit(2)

    try:
    out_file = open(parsed_args.out_file, "wb")
    except IOError as e:
    print("Could not open output file `%s' for writing: %s" % (parsed_args.out_file, e), file=sys.stderr)
    sys.exit(3)
    return p


    def main():
    args = get_argparser().parse_args()

    try:
    if parsed_args.decompress:
    data = decompress(in_file)
    else:
    data = compress(in_file)
    with args.in_file as fh:
    if args.decompress:
    data = decompress(fh)
    else:
    data = compress(fh)
    except Exception as e:
    print("Could not compress/decompress file `%s': %s" % (parsed_args.in_file, e), file=sys.stderr)
    print(
    "Could not compress/decompress file `%s': %s" % (
    args.in_file.name,
    e
    ),
    file=sys.stderr
    )
    sys.exit(4)

    try:
    out_file.write(data)
    except IOError as e:
    print("Could not write to output file `%s': %s" % (parsed_args.out_file, e), file=sys.stderr)
    with args.out_file as fh:
    fh.write(data)
    except Exception as e:
    print(
    "Could not write to output file `%s': %s" % (
    args.out_file.name,
    e
    ),
    file=sys.stderr
    )
    sys.exit(5)
    finally:
    out_file.close()


    if __name__ == "__main__":
    sys.exit(main())
  3. Tblue revised this gist Jul 24, 2015. 1 changed file with 2 additions and 7 deletions.
    9 changes: 2 additions & 7 deletions mozlz4a.py
    Original file line number Diff line number Diff line change
    @@ -34,7 +34,6 @@
    import sys

    from argparse import ArgumentParser
    from io import BytesIO


    class MozLz4aError(Exception):
    @@ -56,12 +55,8 @@ def decompress(file_obj):
    return lz4.decompress(file_obj.read())

    def compress(file_obj):
    out = BytesIO()

    out.write(b"mozLz40\0")
    out.write(lz4.compress(file_obj.read()))

    return out.getbuffer()
    compressed = lz4.compress(file_obj.read())
    return b"mozLz40\0" + compressed


    if __name__ == "__main__":
  4. Tblue revised this gist Jul 23, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion mozlz4a.py
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@
    # Decompressor/compressor for files in Mozilla's "mozLz4" format. Firefox uses this file format to
    # compress e. g. bookmark backups (*.jsonlz4).
    #
    # This file format is in fact just LZ4 data with a custom header (magic number [8 bytes] and
    # This file format is in fact just plain LZ4 data with a custom header (magic number [8 bytes] and
    # uncompressed file size [4 bytes, little endian]).
    #
    # This Python 3 script requires the LZ4 bindings for Python, see: https://pypi.python.org/pypi/lz4
  5. Tblue revised this gist Jul 23, 2015. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions mozlz4a.py
    Original file line number Diff line number Diff line change
    @@ -3,10 +3,10 @@
    # Decompressor/compressor for files in Mozilla's "mozLz4" format. Firefox uses this file format to
    # compress e. g. bookmark backups (*.jsonlz4).
    #
    # This file format is in fact just an LZ4 stream with a custom header (magic number [8 bytes] and
    # This file format is in fact just LZ4 data with a custom header (magic number [8 bytes] and
    # uncompressed file size [4 bytes, little endian]).
    #
    # This script requires the LZ4 bindings for Python, see: https://pypi.python.org/pypi/lz4
    # This Python 3 script requires the LZ4 bindings for Python, see: https://pypi.python.org/pypi/lz4
    #
    #
    # Copyright (c) 2015, Tilman Blumenbach
  6. Tblue created this gist Jul 23, 2015.
    113 changes: 113 additions & 0 deletions mozlz4a.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,113 @@
    #!/usr/bin/env python
    #
    # Decompressor/compressor for files in Mozilla's "mozLz4" format. Firefox uses this file format to
    # compress e. g. bookmark backups (*.jsonlz4).
    #
    # This file format is in fact just an LZ4 stream with a custom header (magic number [8 bytes] and
    # uncompressed file size [4 bytes, little endian]).
    #
    # This script requires the LZ4 bindings for Python, see: https://pypi.python.org/pypi/lz4
    #
    #
    # Copyright (c) 2015, Tilman Blumenbach
    # All rights reserved.
    #
    # Redistribution and use in source and binary forms, with or without modification, are permitted
    # provided that the following conditions are met:
    #
    # 1. Redistributions of source code must retain the above copyright notice, this list of conditions
    # and the following disclaimer.
    # 2. Redistributions in binary form must reproduce the above copyright notice, this list of
    # conditions and the following disclaimer in the documentation and/or other materials provided
    # with the distribution.
    #
    # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
    # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
    # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
    # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    import lz4
    import sys

    from argparse import ArgumentParser
    from io import BytesIO


    class MozLz4aError(Exception):
    pass


    class InvalidHeader(MozLz4aError):
    def __init__(self, msg):
    self.msg = msg

    def __str__(self):
    return self.msg


    def decompress(file_obj):
    if file_obj.read(8) != b"mozLz40\0":
    raise InvalidHeader("Invalid magic number")

    return lz4.decompress(file_obj.read())

    def compress(file_obj):
    out = BytesIO()

    out.write(b"mozLz40\0")
    out.write(lz4.compress(file_obj.read()))

    return out.getbuffer()


    if __name__ == "__main__":
    argparser = ArgumentParser(description="MozLz4a compression/decompression utility")
    argparser.add_argument(
    "-d", "--decompress", "--uncompress",
    action="store_true",
    help="Decompress the input file instead of compressing it."
    )
    argparser.add_argument(
    "in_file",
    help="Path to input file."
    )
    argparser.add_argument(
    "out_file",
    help="Path to output file."
    )

    parsed_args = argparser.parse_args()


    try:
    in_file = open(parsed_args.in_file, "rb")
    except IOError as e:
    print("Could not open input file `%s' for reading: %s" % (parsed_args.in_file, e), file=sys.stderr)
    sys.exit(2)

    try:
    out_file = open(parsed_args.out_file, "wb")
    except IOError as e:
    print("Could not open output file `%s' for writing: %s" % (parsed_args.out_file, e), file=sys.stderr)
    sys.exit(3)

    try:
    if parsed_args.decompress:
    data = decompress(in_file)
    else:
    data = compress(in_file)
    except Exception as e:
    print("Could not compress/decompress file `%s': %s" % (parsed_args.in_file, e), file=sys.stderr)
    sys.exit(4)

    try:
    out_file.write(data)
    except IOError as e:
    print("Could not write to output file `%s': %s" % (parsed_args.out_file, e), file=sys.stderr)
    sys.exit(5)
    finally:
    out_file.close()