/* * * Copyright (C) 2002-2010, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by * * OFFIS e.V. * R&D Division Health * Escherweg 2 * D-26121 Oldenburg, Germany * * * Module: dcmdata * * Author: Marco Eichelberg * * Purpose: DcmInputBufferStream and related classes, * implements input to blocks of memory as needed in the dcmnet module. * */ #include "dcmtk/config/osconfig.h" #include "dcmtk/dcmdata/dcistrmb.h" #include "dcmtk/dcmdata/dcerror.h" #define DCMBUFFERPRODUCER_BUFSIZE 1024 DcmBufferProducer::DcmBufferProducer() : DcmProducer() , buffer_(NULL) , backup_(new unsigned char[DCMBUFFERPRODUCER_BUFSIZE]) , bufSize_(0) , bufIndex_(0) , backupIndex_(DCMBUFFERPRODUCER_BUFSIZE) , backupStart_(DCMBUFFERPRODUCER_BUFSIZE) , status_(EC_Normal) , eosflag_(OFFalse) { if (!backup_) status_ = EC_MemoryExhausted; } DcmBufferProducer::~DcmBufferProducer() { delete[] backup_; } OFBool DcmBufferProducer::good() const { return status_.good(); } OFCondition DcmBufferProducer::status() const { return status_; } OFBool DcmBufferProducer::eos() { // end of stream is true if the user has called setEos() before // and there is no more data available in the current buffer. // We also flag end of stream if the status is bad. return (eosflag_ && (avail() == 0)) || (!status_.good()); } offile_off_t DcmBufferProducer::avail() { if (status_.good()) { // in the backup buffer, we have (DCMBUFFERPRODUCER_BUFSIZE - backupIndex_) // bytes available. In the user buffer, we have (bufSize_ - bufIndex_). return DCMBUFFERPRODUCER_BUFSIZE + bufSize_ - bufIndex_ - backupIndex_; } else return 0; } offile_off_t DcmBufferProducer::read(void *buf, offile_off_t buflen) { offile_off_t result = 0; if (status_.good() && buflen && buf) { unsigned char *target = OFstatic_cast(unsigned char *, buf); if (backupIndex_ < DCMBUFFERPRODUCER_BUFSIZE) { // we have data in the backup buffer, read first result = DCMBUFFERPRODUCER_BUFSIZE - backupIndex_; if (result > buflen) result = buflen; memcpy(target, backup_ + backupIndex_, OFstatic_cast(size_t, result)); backupIndex_ += result; target += result; buflen -= result; } if (buflen && bufSize_) { // read data from user buffer offile_off_t numbytes = bufSize_ - bufIndex_; if (numbytes > buflen) numbytes = buflen; memcpy(target, buffer_ + bufIndex_, OFstatic_cast(size_t, numbytes)); bufIndex_ += numbytes; result += numbytes; } } return result; } offile_off_t DcmBufferProducer::skip(offile_off_t skiplen) { offile_off_t result = 0; if (status_.good() && skiplen) { if (backupIndex_ < DCMBUFFERPRODUCER_BUFSIZE) { // we have data in the backup buffer, skip first result = DCMBUFFERPRODUCER_BUFSIZE - backupIndex_; if (result > skiplen) result = skiplen; backupIndex_ += result; skiplen -= result; } if (skiplen && bufSize_) { // skip data from user buffer offile_off_t skipbytes = bufSize_ - bufIndex_; if (skipbytes > skiplen) skipbytes = skiplen; bufIndex_ += skipbytes; result += skipbytes; } } return result; } void DcmBufferProducer::putback(offile_off_t num) { if (status_.good() && num) { if (bufSize_ && bufIndex_) { // since bufIndex_ > 0, data has already been read from the user buffer. // This means we should putback in the user buffer first, and only // if this is not sufficient we also touch the backup buffer. if (num > bufIndex_) { num -= bufIndex_; bufIndex_ = 0; } else { bufIndex_ -= num; num = 0; } } if (num && (backupIndex_ > backupStart_)) { // there is still a number of bytes to putback, and we have data in the // backup buffer, so we can actually putback something there. // This will cause the next read operation to read from the backup // buffer first and only then access the user buffer. if (num > (backupIndex_ - backupStart_)) { num -= backupIndex_ - backupStart_; backupIndex_ = backupStart_; } else { backupIndex_ -= num; num = 0; } } if (num) { // we didn't manage to execute the putback request because there was // not enough data available in both buffers. Producer failure. status_ = EC_PutbackFailed; } } } void DcmBufferProducer::setBuffer(const void *buf, offile_off_t buflen) { if (status_.good()) { if (buffer_ || eosflag_) { // error: attempt to set new buffer without calling releaseBuffer before // or after calling setEos. status_ = EC_IllegalCall; } else if (buf && buflen) { buffer_ = OFstatic_cast(unsigned char *, OFconst_cast(void *, buf)); bufSize_ = buflen; bufIndex_ = 0; } } } void DcmBufferProducer::releaseBuffer() { // releaseBuffer() might be called multiple times, so buffer_ could already be NULL. if (status_.good() && buffer_) { // compute the least number of bytes that we have to store in the backup buffer offile_off_t numBytes = bufSize_ - bufIndex_; if (numBytes > backupIndex_) { // number of bytes is larger than free space in backup buffer; fail. status_ = EC_IllegalCall; } else { // if number of bytes is smaller than free space in backup buffer, make as large as possible if (numBytes < backupIndex_) { numBytes = (backupIndex_ < bufSize_) ? backupIndex_ : bufSize_; } // if number of bytes is smaller than backup buffer, move old // data in backup buffer to keep older data available for putback operations if (numBytes < DCMBUFFERPRODUCER_BUFSIZE) { // move (DCMBUFFERPRODUCER_BUFSIZE - numBytes) bytes from end of backup buffer // to start of backup buffer. Everything else will be overwritten from the // user buffer. memmove(backup_, backup_ + numBytes, OFstatic_cast(size_t, DCMBUFFERPRODUCER_BUFSIZE - numBytes)); // adjust backupStart_ if (backupStart_ < numBytes) backupStart_ = 0; else backupStart_ -= numBytes; } else { // the backup buffer will be filled completely from the user buffer backupStart_ = 0; } // copy (numBytes) bytes from the end of the user buffer to the end of the backup buffer memcpy(backup_ + DCMBUFFERPRODUCER_BUFSIZE - numBytes, buffer_ + bufSize_ - numBytes, OFstatic_cast(size_t, numBytes)); // adjust backupIndex_ if (backupIndex_ == DCMBUFFERPRODUCER_BUFSIZE) { // there was no unread data in the backup buffer before. // backupIndex_ only depends on the number of unread bytes in the user buffer. // we know that (bufSize_ - bufIndex_ < DCMBUFFERPRODUCER_BUFSIZE) because this was tested before. backupIndex_ = DCMBUFFERPRODUCER_BUFSIZE + bufIndex_ - bufSize_; } else { // there was unread data in the backup buffer before. // This implies that all of the user buffer is unread and the complete user // buffer fits into the free space in the backup buffer, because otherwise // we would not have got this far. // Adjust backupIndex_ by the number of bytes we have moved the content of the backup buffer. backupIndex_ -= numBytes; } // release user buffer buffer_ = NULL; bufSize_ = 0; bufIndex_ = 0; } } // the number of bytes that can be putback after this operation depends // on the size of the backup buffer and on the number of unread bytes // in both buffers at the time of the releaseBuffer() operation. // If the user only calls releaseBuffer() when most data has been read // from the buffer, we should be able to putback almost 1K. } void DcmBufferProducer::setEos() { eosflag_ = OFTrue; } /* ======================================================================= */ DcmInputBufferStream::DcmInputBufferStream() : DcmInputStream(&producer_) // safe because DcmInputStream only stores pointer , producer_() { } DcmInputBufferStream::~DcmInputBufferStream() { #ifdef DEBUG if ((!eos()) && (avail() > 0)) { DCMDATA_WARN("closing unflushed DcmInputBufferStream, loss of data!"); } #endif } DcmInputStreamFactory *DcmInputBufferStream::newFactory() const { // we don't support delayed loading from buffer streams return NULL; } void DcmInputBufferStream::setBuffer(const void *buf, offile_off_t buflen) { producer_.setBuffer(buf, buflen); // if there is a compression filter, the following call will // cause it to feed the compression engine with data from the // new buffer. skip(0); } void DcmInputBufferStream::releaseBuffer() { producer_.releaseBuffer(); } void DcmInputBufferStream::setEos() { producer_.setEos(); }