/* * Copyright (c) 2012, Chris Johns * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * @ingroup rtems_ld * * @brief RTEMS Linker. * */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "fastlz.h" namespace rld { namespace compress { compressor::compressor (files::image& image, size_t size, bool out, bool compress) : image (image), size (size), out (out), compress (compress), buffer (0), io (0), level (0), total (0), total_compressed (0) { if (size > 0xffff) throw rld::error ("Size too big, 16 bits only", "compression"); buffer = new uint8_t[size]; io = new uint8_t[size + (size / 10)]; } compressor::~compressor () { flush (); delete [] buffer; delete [] io; } void compressor::write (const void* data_, size_t length) { if (!out) throw rld::error ("Write on read-only", "compression"); const uint8_t* data = static_cast (data_); while (length) { size_t appending; if (length > (size - level)) appending = size - level; else appending = length; ::memcpy ((void*) (buffer + level), data, appending); data += appending; level += appending; length -= appending; total += appending; output (); } } void compressor::write (files::image& input, off_t offset, size_t length) { if (!out) throw rld::error ("Write on read-only", "compression"); input.seek (offset); while (length) { size_t appending; if (length > (size - level)) appending = size - level; else appending = length; input.read ((void*) (buffer + level), appending); level += appending; length -= appending; total += appending; output (); } } size_t compressor::read (void* data_, size_t length) { if (out) throw rld::error ("Read on write-only", "compression"); uint8_t* data = static_cast (data_); size_t amount = 0; while (length) { input (); if (level == 0) break; size_t appending; if (length > level) appending = level; else appending = length; ::memcpy (data, buffer, appending); ::memmove (buffer, buffer + appending, level - appending); data += appending; level -= appending; length -= appending; total += appending; amount += appending; } return amount; } size_t compressor::read (files::image& output_, off_t offset, size_t length) { if (out) throw rld::error ("Read on write-only", "compression"); output_.seek (offset); return read (output_, length); } size_t compressor::read (files::image& output_, size_t length) { if (out) throw rld::error ("Read on write-only", "compression"); size_t amount = 0; while (length) { input (); if (level == 0) break; size_t appending; if (length > level) appending = level; else appending = length; output_.write (buffer, appending); ::memmove (buffer, buffer + appending, level - appending); level -= appending; length -= appending; total += appending; amount += appending; } return amount; } void compressor::flush () { output (true); } size_t compressor::transferred () const { return total; } size_t compressor::compressed () const { return total_compressed; } off_t compressor::offset () const { return total; } void compressor::output (bool forced) { if (out && ((forced && level) || (level >= size))) { if (compress) { int writing = ::fastlz_compress (buffer, level, io); uint8_t header[2]; if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) std::cout << "rtl: comp: offset=" << total_compressed << " block-size=" << writing << std::endl; header[0] = writing >> 8; header[1] = writing; image.write (header, 2); image.write (io, writing); total_compressed += 2 + writing; } else { image.write (buffer, level); } level = 0; } } void compressor::input () { if (!out && (level == 0)) { if (compress) { uint8_t header[2]; if (image.read (header, 2) == 2) { ssize_t block_size = (((ssize_t) header[0]) << 8) | (ssize_t) header[1]; if (block_size == 0) throw rld::error ("Block size is invalid (0)", "compression"); total_compressed += 2 + block_size; if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) std::cout << "rtl: decomp: block-size=" << block_size << std::endl; if (image.read (io, block_size) != block_size) throw rld::error ("Read past end", "compression"); level = ::fastlz_decompress (io, block_size, buffer, size); } } else { image.read (buffer, size); level = size; } } } } }