From d61f6d5e6e24f6300a5dc2ca8adfef5b157b5463 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 8 Dec 2020 12:54:20 -0500 Subject: [PATCH] Switch to faster EFI decompressor The pure python EFI decompressor was extraordinarily slow. Dramatically improve performance by switching to the C version of the function. Change-Id: Ib931d46a4b5b4937cb8dfc3d37bfa63b92e84ead --- pyghmi/ipmi/oem/lenovo/EfiDecompressor.py | 199 ---------------------- pyghmi/ipmi/oem/lenovo/config.py | 3 +- 2 files changed, 1 insertion(+), 201 deletions(-) delete mode 100644 pyghmi/ipmi/oem/lenovo/EfiDecompressor.py diff --git a/pyghmi/ipmi/oem/lenovo/EfiDecompressor.py b/pyghmi/ipmi/oem/lenovo/EfiDecompressor.py deleted file mode 100644 index f6281580..00000000 --- a/pyghmi/ipmi/oem/lenovo/EfiDecompressor.py +++ /dev/null @@ -1,199 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import struct - -try: - range = xrange -except NameError: - pass - - -class BitArray(object): - def __init__(self, data): - self._Data = bytearray(data) - - self._DataBitsLeft = len(data) * 8 - - self._ByteIdx = 0 - self._BitIdx = 0 - - @staticmethod - def mask(bitcount): - return (1 << bitcount) - 1 - - def read(self, bitsleftcount): - - result = 0 - while bitsleftcount: - curbitsleftcount = 8 - self._BitIdx - curdata = self._Data[self._ByteIdx] & self.mask( - curbitsleftcount) - - if curbitsleftcount >= bitsleftcount: - result <<= bitsleftcount - result |= curdata >> (curbitsleftcount - bitsleftcount) - self._BitIdx += bitsleftcount - bitsleftcount = 0 - else: - result <<= curbitsleftcount - result |= curdata - bitsleftcount -= curbitsleftcount - self._BitIdx += curbitsleftcount - - if self._BitIdx >= 8: - self._BitIdx = 0 - self._ByteIdx += 1 - - return result - - -def loadhuffmansyms(bits, symscountbits, zeroskipidx): - symscount = bits.read(symscountbits) - if symscount == 0: - v = bits.read(symscountbits) - huffsyms = [[v, 1, 0], [v, 1, 1]] - else: - # Decode the horrible bit length encoding thing! - huffsyms = [] - idx = 0 - while idx < symscount: - bitlen = bits.read(3) - if bitlen == 7: - while bits.read(1): - bitlen += 1 - if bitlen != 0: - huffsyms += ([idx, bitlen, None],) - idx += 1 - - # decode the extra special nasty zero-skip hack! - if idx == zeroskipidx: - idx += bits.read(2) - - # Now, sort them by bit length - huffsyms = sorted(huffsyms, key=lambda length: length[1]) - - # Allocate huffman codes to the length-ordered symbols - huffsyms[0][2] = 0 - for idx in range(1, len(huffsyms)): - huffsyms[idx][2] = (huffsyms[idx - 1][2] + 1) << ( - huffsyms[idx][1] - huffsyms[idx - 1][1]) - - return huffsyms - - -def buildhuffmantree(huffsyms): - hufftree = [None, None] - for huffsym in huffsyms: - symbol = huffsym[0] - bitlen = huffsym[1] - huffcode = huffsym[2] - if bitlen == 0: - continue - - huffsubtree = hufftree - for bit in range(0, bitlen): - lr = huffcode & (1 << (bitlen - bit - 1)) != 0 - - if bit < bitlen - 1: - if huffsubtree[lr] is None: - huffsubtree[lr] = [None, None] - huffsubtree = huffsubtree[lr] - else: - huffsubtree[lr] = symbol - return hufftree - - -def huffmandecode(hufftree, bits): - while type(hufftree) == list: - hufftree = hufftree[bits.read(1)] - return hufftree - - -def loadcharlenhuffmansyms(bits, extra_hufftree): - symscount = bits.read(9) - - if symscount == 0: - v = bits.read(9) - huffsyms = [[v, 1, 0], [v, 1, 1]] - else: - # Decode the horrible bit length encoding thing! - huffsyms = [] - idx = 0 - while idx < symscount: - bitlen = huffmandecode(extra_hufftree, bits) - - if bitlen == 0: - bitlen = 0 - elif bitlen == 1: - idx += bits.read(4) + 3 - 1 - bitlen = 0 - elif bitlen == 2: - idx += bits.read(9) + 20 - 1 - bitlen = 0 - else: - bitlen -= 2 - - if bitlen != 0: - huffsyms += ([idx, bitlen, None],) - - idx += 1 - - # Now, sort them by bit length - huffsyms = sorted(huffsyms, key=lambda length: length[1]) - - # Allocate huffman codes to the length-ordered symbols - huffsyms[0][2] = 0 - for idx in range(1, len(huffsyms)): - huffsyms[idx][2] = (huffsyms[idx - 1][2] + 1) << ( - huffsyms[idx][1] - huffsyms[idx - 1][1]) - - return huffsyms - - -def decompress(buf): - (compressed_size, decompressed_size) = struct.unpack(" 1: - data_offset = (1 << (pos_bitlen - 1)) + bits.read( - pos_bitlen - 1) - data_idx = len(outbuf) - data_offset - 1 - - for i in range(0, data_length): - outbuf.append(outbuf[data_idx + i]) - decompressed_size -= data_length - - return bytes(outbuf) diff --git a/pyghmi/ipmi/oem/lenovo/config.py b/pyghmi/ipmi/oem/lenovo/config.py index ef6be06b..3d1370ad 100644 --- a/pyghmi/ipmi/oem/lenovo/config.py +++ b/pyghmi/ipmi/oem/lenovo/config.py @@ -24,7 +24,6 @@ import struct import six import pyghmi.exceptions as pygexc -from pyghmi.ipmi.oem.lenovo import EfiDecompressor try: import EfiCompressor @@ -260,7 +259,7 @@ class LenovoFirmwareConfig(object): size = self.imm_size(cfgfilename) data = self.imm_read(filehandle, size) self.imm_close(filehandle) - data = EfiDecompressor.decompress(data) + data = EfiCompressor.FrameworkDecompress(data, len(data)) if len(data) != 0: break self.connection.ipmi_session.pause(2)