<commit_before>/*
 * Copyright 2010 Google Inc.
 *
 * 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.
 */

// Author: jmarantz@google.com (Joshua Marantz)
//
// Contains implementation of CssCombineFilter, which concatenates multiple
// CSS files into one. Implemented in part via delegating to
// CssCombineFilter::CssCombiner, a ResourceCombiner subclass.

#include "net/instaweb/rewriter/public/css_combine_filter.h"

#include <vector>

#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "net/instaweb/htmlparse/public/doctype.h"
#include "net/instaweb/htmlparse/public/html_element.h"
#include "net/instaweb/htmlparse/public/html_name.h"
#include "net/instaweb/http/public/content_type.h"
#include "net/instaweb/rewriter/cached_result.pb.h"
#include "net/instaweb/rewriter/public/css_tag_scanner.h"
#include "net/instaweb/rewriter/public/output_resource.h"
#include "net/instaweb/rewriter/public/output_resource_kind.h"
#include "net/instaweb/rewriter/public/resource.h"
#include "net/instaweb/rewriter/public/resource_combiner.h"
#include "net/instaweb/rewriter/public/resource_manager.h"
#include "net/instaweb/rewriter/public/resource_slot.h"
#include "net/instaweb/rewriter/public/rewrite_context.h"
#include "net/instaweb/rewriter/public/rewrite_driver.h"
#include "net/instaweb/rewriter/public/rewrite_filter.h"
#include "net/instaweb/rewriter/public/rewrite_result.h"
#include "net/instaweb/util/public/charset_util.h"
#include "net/instaweb/util/public/google_url.h"
#include "net/instaweb/util/public/message_handler.h"
#include "net/instaweb/util/public/proto_util.h"
#include "net/instaweb/util/public/ref_counted_ptr.h"
#include "net/instaweb/util/public/statistics.h"
#include "net/instaweb/util/public/string.h"
#include "net/instaweb/util/public/string_util.h"
#include "net/instaweb/util/public/writer.h"
#include "webutil/css/parser.h"

namespace net_instaweb {

class HtmlIEDirectiveNode;
class UrlSegmentEncoder;

// names for Statistics variables.
const char CssCombineFilter::kCssFileCountReduction[] =
    "css_file_count_reduction";

// Combining helper. Takes care of checking that media matches, that we do not
// produce @import's in the middle and of URL absolutification.
class CssCombineFilter::CssCombiner : public ResourceCombiner {
 public:
  CssCombiner(RewriteDriver* driver,
              CssTagScanner* css_tag_scanner,
              CssCombineFilter* filter)
      : ResourceCombiner(driver, kContentTypeCss.file_extension() + 1, filter),
        css_tag_scanner_(css_tag_scanner) {
    Statistics* stats = resource_manager_->statistics();
    css_file_count_reduction_ = stats->GetVariable(kCssFileCountReduction);
  }

  bool CleanParse(const StringPiece& contents) {
    Css::Parser parser(contents);
    // Note: We do not turn on preservation_mode because that could pass through
    // verbatim text that will break other CSS files combined with this one.
    // TODO(sligocki): Be less conservative here and actually scan verbatim
    // text for bad constructs (Ex: contains "{").
    // TODO(sligocki): Do parsing on low-priority worker thread.
    scoped_ptr<Css::Stylesheet> stylesheet(parser.ParseRawStylesheet());
    return (parser.errors_seen_mask() == Css::Parser::kNoError);
  }

  virtual bool ResourceCombinable(Resource* resource, MessageHandler* handler) {
    // If this CSS file is not parseable it may have errors that will break
    // the rest of the files combined with this one. So we should not include
    // it in the combination.
    // TODO(sligocki): Just do the CSS parsing and rewriting here.
    if (!CleanParse(resource->contents())) {
      handler->Message(kInfo, "Failed to combine %s because of parse error.",
                       resource->url().c_str());
      // TODO(sligocki): All parse failures are repeated twice because we will
      // try to combine them in the normal combination, then we'll try again
      // with this as the first of a new combination.
      return false;
    }

    // styles containing @import cannot be appended to others, as any
    // @import in the middle will be ignored.
    // TODO(sligocki): Do CSS parsing and rewriting here so that we can
    // git rid of this restriction.
    return ((num_urls() == 0)
            || !CssTagScanner::HasImport(resource->contents(), handler));
  }

  OutputResourcePtr MakeOutput() {
    return Combine(rewrite_driver_->message_handler());
  }

  bool Write(const ResourceVector& in, const OutputResourcePtr& out) {
    return WriteCombination(in, out, rewrite_driver_->message_handler());
  }

  void set_media(const char* media) { media_ = media; }
  const GoogleString& media() const { return media_; }

  void AddFileCountReduction(int num_files) {
    css_file_count_reduction_->Add(num_files);
  }

 private:
  virtual const ContentType* CombinationContentType() {
    return &kContentTypeCss;
  }

  virtual bool WritePiece(int index, const Resource* input,
                          OutputResource* combination, Writer* writer,
                          MessageHandler* handler);

  GoogleString media_;
  CssTagScanner* css_tag_scanner_;
  Variable* css_file_count_reduction_;
};

class CssCombineFilter::Context : public RewriteContext {
 public:
  Context(RewriteDriver* driver, CssTagScanner* scanner,
          CssCombineFilter* filter)
      : RewriteContext(driver, NULL, NULL),
        filter_(filter),
        combiner_(driver, scanner, filter),
        new_combination_(true) {
  }

  CssCombiner* combiner() { return &combiner_; }

  bool AddElement(HtmlElement* element, HtmlElement::Attribute* href) {
    bool ret = false;
    ResourcePtr resource(filter_->CreateInputResource(
        href->DecodedValueOrNull()));
    if (resource.get() != NULL) {
      ResourceSlotPtr slot(Driver()->GetSlot(resource, element, href));
      AddSlot(slot);
      elements_.push_back(element);
      ret = true;
    }
    return ret;
  }

  bool empty() const { return elements_.empty(); }
  bool new_combination() const { return new_combination_; }

  void Reset() {
    combiner_.Reset();
    combiner_.set_media("");
    new_combination_ = true;
  }

  void SetMedia(const char* media) {
    combiner_.set_media(media);
    new_combination_ = false;
  }

 protected:
  virtual bool Partition(OutputPartitions* partitions,
                         OutputResourceVector* outputs) {
    MessageHandler* handler = Driver()->message_handler();
    CachedResult* partition = NULL;
    CHECK_EQ(static_cast<int>(elements_.size()), num_slots());
    for (int i = 0, n = num_slots(); i < n; ++i) {
      bool add_input = false;
      ResourcePtr resource(slot(i)->resource());

      if (resource->IsValidAndCacheable()) {
        if (combiner_.AddResourceNoFetch(resource, handler).value) {
          // This new element works in the existing partition.
          add_input = true;
        } else {
          // This new element does not work in the existing partition,
          // so close out that partition if it's non-empty.
          if (partition != NULL) {
            FinalizePartition(partitions, partition, outputs);
            partition = NULL;
            if (combiner_.AddResourceNoFetch(resource, handler).value) {
              add_input = true;
            }
          }
        }
      } else {
        // A failed resource-fetch tells us to finalize any partition that
        // we've already started.  We don't want to combine across a CSS file
        // that our server sees as a 404 because the browser might successfully
        // fetch that file, and thus we'd mangle the ordering if we combined
        // across it.
        FinalizePartition(partitions, partition, outputs);
        partition = NULL;
      }
      if (add_input) {
        if (partition == NULL) {
          partition = partitions->add_partition();
        }
        resource->AddInputInfoToPartition(
            Resource::kIncludeInputHash, i, partition);
      }
    }
    FinalizePartition(partitions, partition, outputs);
    return (partitions->partition_size() != 0);
  }

  virtual void Rewrite(int partition_index,
                       CachedResult* partition,
                       const OutputResourcePtr& output) {
    // resource_combiner.cc calls WriteCombination as part
    // of Combine.  But if we are being called on behalf of a
    // fetch then the resource still needs to be written.
    RewriteResult result = kRewriteOk;
    // OutputResource CHECK-fails if you try to Write twice, which
    // would happen in the html-rewrite phase without this check.
    if (!output->IsWritten()) {
      ResourceVector resources;
      for (int i = 0, n = num_slots(); i < n; ++i) {
        ResourcePtr resource(slot(i)->resource());
        resources.push_back(resource);
      }
      if (!combiner_.Write(resources, output)) {
        result = kRewriteFailed;
      }
    }
    RewriteDone(result, partition_index);
  }

  virtual void Render() {
    // Slot 0 will be replaced by the combined resource as part of
    // rewrite_context.cc.  But we still need to delete slots 1-N.
    for (int p = 0, np = num_output_partitions(); p < np; ++p) {
      CachedResult* partition = output_partition(p);
      if (partition->input_size() == 0) {
        continue;
      }

      if (filter_->driver()->doctype().IsXhtml()) {
        int first_element_index = partition->input(0).index();
        HtmlElement* first_element = elements_[first_element_index];
        first_element->set_close_style(HtmlElement::BRIEF_CLOSE);
      }
      for (int i = 1; i < partition->input_size(); ++i) {
        int slot_index = partition->input(i).index();
        slot(slot_index)->set_should_delete_element(true);
      }
      combiner_.AddFileCountReduction(partition->input_size() - 1);
    }
  }

  virtual const UrlSegmentEncoder* encoder() const {
    return filter_->encoder();
  }
  virtual const char* id() const { return filter_->id(); }
  virtual OutputResourceKind kind() const { return kRewrittenResource; }

 private:
  void FinalizePartition(OutputPartitions* partitions,
                         CachedResult* partition,
                         OutputResourceVector* outputs) {
    if (partition != NULL) {
      OutputResourcePtr combination_output(combiner_.MakeOutput());
      if (combination_output.get() == NULL) {
        partitions->mutable_partition()->RemoveLast();
      } else {
        combination_output->UpdateCachedResultPreservingInputInfo(partition);
        outputs->push_back(combination_output);
      }
      Reset();
    }
  }

  std::vector<HtmlElement*> elements_;
  RewriteFilter* filter_;
  CssCombineFilter::CssCombiner combiner_;
  bool new_combination_;
  DISALLOW_COPY_AND_ASSIGN(Context);
};

// TODO(jmarantz) We exhibit zero intelligence about which css files to
// combine; we combine whatever is possible.  This can reduce performance
// by combining highly cacheable shared resources with transient ones.
//
// TODO(jmarantz): We do not recognize IE directives as spriting boundaries.
// We should supply a meaningful IEDirective method as a boundary.
//
// TODO(jmarantz): allow combining of CSS elements found in the body, whether
// or not the head has already been flushed.
//
// TODO(jmaessen): The addition of 1 below avoids the leading ".";
// make this convention consistent and fix all code.
CssCombineFilter::CssCombineFilter(RewriteDriver* driver)
    : RewriteFilter(driver),
      css_tag_scanner_(driver_) {
}

CssCombineFilter::~CssCombineFilter() {
}

void CssCombineFilter::Initialize(Statistics* statistics) {
  statistics->AddVariable(kCssFileCountReduction);
}

void CssCombineFilter::StartDocumentImpl() {
  context_.reset(MakeContext());
}

void CssCombineFilter::StartElementImpl(HtmlElement* element) {
  HtmlElement::Attribute* href;
  const char* media;
  if (!driver_->HasChildrenInFlushWindow(element) &&
      css_tag_scanner_.ParseCssElement(element, &href, &media)) {
    // We cannot combine with a link in <noscript> tag and we cannot combine
    // over a link in a <noscript> tag, so this is a barrier.
    if (noscript_element() != NULL) {
      NextCombination();
    } else {
      if (context_->new_combination()) {
        context_->SetMedia(media);
      } else if (combiner()->media() != media) {
        // After the first CSS file, subsequent CSS files must have matching
        // media.
        // TODO(jmarantz): do media='' and media='display mean the same
        // thing?  sligocki thinks mdsteele looked into this and it
        // depended on HTML version.  In one display was default, in the
        // other screen was IIRC.
        NextCombination();
        context_->SetMedia(media);
      }
      if (!context_->AddElement(element, href)) {
        NextCombination();
      }
    }
  } else if (element->keyword() == HtmlName::kStyle) {
    // We can't reorder styles on a page, so if we are only combining <link>
    // tags, we can't combine them across a <style> tag.
    // TODO(sligocki): Maybe we should just combine <style>s too?
    // We can run outline_css first for now to make all <style>s into <link>s.
    NextCombination();
  }
}

void CssCombineFilter::NextCombination() {
  if (!context_->empty()) {
    driver_->InitiateRewrite(context_.release());
    context_.reset(MakeContext());
  }
  context_->Reset();
}

// An IE directive that includes any stylesheet info should be a barrier
// for css combining.  It's OK to emit the combination we've seen so far.
void CssCombineFilter::IEDirective(HtmlIEDirectiveNode* directive) {
  // TODO(sligocki): Figure out how to safely parse IEDirectives, for now we
  // just consider them black boxes / solid barriers.
  NextCombination();
}

void CssCombineFilter::Flush() {
  NextCombination();
}

bool CssCombineFilter::CssCombiner::WritePiece(
    int index, const Resource* input, OutputResource* combination,
    Writer* writer, MessageHandler* handler) {
  StringPiece contents = input->contents();
  GoogleUrl input_url(input->url());
  StringPiece input_dir = input_url.AllExceptLeaf();
  // Strip the BOM off of the contents (if it's there) if this is not the
  // first resource.
  if (index != 0) {
    StripUtf8Bom(&contents);
  }
  bool ret = false;
  switch (rewrite_driver_->ResolveCssUrls(
      input_url, combination->resolved_base(), contents, writer, handler)) {
    case RewriteDriver::kNoResolutionNeeded:
      ret = writer->Write(contents, handler);
      break;
    case RewriteDriver::kWriteFailed:
      break;
    case RewriteDriver::kSuccess:
      ret = true;
      break;
  }
  return ret;
}

CssCombineFilter::CssCombiner* CssCombineFilter::combiner() {
  return context_->combiner();
}

CssCombineFilter::Context* CssCombineFilter::MakeContext() {
  return new Context(driver_, &css_tag_scanner_, this);
}

RewriteContext* CssCombineFilter::MakeRewriteContext() {
  return MakeContext();
}

}  // namespace net_instaweb
<commit_msg>remove unused temp variable.<commit_after>/*
 * Copyright 2010 Google Inc.
 *
 * 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.
 */

// Author: jmarantz@google.com (Joshua Marantz)
//
// Contains implementation of CssCombineFilter, which concatenates multiple
// CSS files into one. Implemented in part via delegating to
// CssCombineFilter::CssCombiner, a ResourceCombiner subclass.

#include "net/instaweb/rewriter/public/css_combine_filter.h"

#include <vector>

#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "net/instaweb/htmlparse/public/doctype.h"
#include "net/instaweb/htmlparse/public/html_element.h"
#include "net/instaweb/htmlparse/public/html_name.h"
#include "net/instaweb/http/public/content_type.h"
#include "net/instaweb/rewriter/cached_result.pb.h"
#include "net/instaweb/rewriter/public/css_tag_scanner.h"
#include "net/instaweb/rewriter/public/output_resource.h"
#include "net/instaweb/rewriter/public/output_resource_kind.h"
#include "net/instaweb/rewriter/public/resource.h"
#include "net/instaweb/rewriter/public/resource_combiner.h"
#include "net/instaweb/rewriter/public/resource_manager.h"
#include "net/instaweb/rewriter/public/resource_slot.h"
#include "net/instaweb/rewriter/public/rewrite_context.h"
#include "net/instaweb/rewriter/public/rewrite_driver.h"
#include "net/instaweb/rewriter/public/rewrite_filter.h"
#include "net/instaweb/rewriter/public/rewrite_result.h"
#include "net/instaweb/util/public/charset_util.h"
#include "net/instaweb/util/public/google_url.h"
#include "net/instaweb/util/public/message_handler.h"
#include "net/instaweb/util/public/proto_util.h"
#include "net/instaweb/util/public/ref_counted_ptr.h"
#include "net/instaweb/util/public/statistics.h"
#include "net/instaweb/util/public/string.h"
#include "net/instaweb/util/public/string_util.h"
#include "net/instaweb/util/public/writer.h"
#include "webutil/css/parser.h"

namespace net_instaweb {

class HtmlIEDirectiveNode;
class UrlSegmentEncoder;

// names for Statistics variables.
const char CssCombineFilter::kCssFileCountReduction[] =
    "css_file_count_reduction";

// Combining helper. Takes care of checking that media matches, that we do not
// produce @import's in the middle and of URL absolutification.
class CssCombineFilter::CssCombiner : public ResourceCombiner {
 public:
  CssCombiner(RewriteDriver* driver,
              CssTagScanner* css_tag_scanner,
              CssCombineFilter* filter)
      : ResourceCombiner(driver, kContentTypeCss.file_extension() + 1, filter),
        css_tag_scanner_(css_tag_scanner) {
    Statistics* stats = resource_manager_->statistics();
    css_file_count_reduction_ = stats->GetVariable(kCssFileCountReduction);
  }

  bool CleanParse(const StringPiece& contents) {
    Css::Parser parser(contents);
    // Note: We do not turn on preservation_mode because that could pass through
    // verbatim text that will break other CSS files combined with this one.
    // TODO(sligocki): Be less conservative here and actually scan verbatim
    // text for bad constructs (Ex: contains "{").
    // TODO(sligocki): Do parsing on low-priority worker thread.
    scoped_ptr<Css::Stylesheet> stylesheet(parser.ParseRawStylesheet());
    return (parser.errors_seen_mask() == Css::Parser::kNoError);
  }

  virtual bool ResourceCombinable(Resource* resource, MessageHandler* handler) {
    // If this CSS file is not parseable it may have errors that will break
    // the rest of the files combined with this one. So we should not include
    // it in the combination.
    // TODO(sligocki): Just do the CSS parsing and rewriting here.
    if (!CleanParse(resource->contents())) {
      handler->Message(kInfo, "Failed to combine %s because of parse error.",
                       resource->url().c_str());
      // TODO(sligocki): All parse failures are repeated twice because we will
      // try to combine them in the normal combination, then we'll try again
      // with this as the first of a new combination.
      return false;
    }

    // styles containing @import cannot be appended to others, as any
    // @import in the middle will be ignored.
    // TODO(sligocki): Do CSS parsing and rewriting here so that we can
    // git rid of this restriction.
    return ((num_urls() == 0)
            || !CssTagScanner::HasImport(resource->contents(), handler));
  }

  OutputResourcePtr MakeOutput() {
    return Combine(rewrite_driver_->message_handler());
  }

  bool Write(const ResourceVector& in, const OutputResourcePtr& out) {
    return WriteCombination(in, out, rewrite_driver_->message_handler());
  }

  void set_media(const char* media) { media_ = media; }
  const GoogleString& media() const { return media_; }

  void AddFileCountReduction(int num_files) {
    css_file_count_reduction_->Add(num_files);
  }

 private:
  virtual const ContentType* CombinationContentType() {
    return &kContentTypeCss;
  }

  virtual bool WritePiece(int index, const Resource* input,
                          OutputResource* combination, Writer* writer,
                          MessageHandler* handler);

  GoogleString media_;
  CssTagScanner* css_tag_scanner_;
  Variable* css_file_count_reduction_;
};

class CssCombineFilter::Context : public RewriteContext {
 public:
  Context(RewriteDriver* driver, CssTagScanner* scanner,
          CssCombineFilter* filter)
      : RewriteContext(driver, NULL, NULL),
        filter_(filter),
        combiner_(driver, scanner, filter),
        new_combination_(true) {
  }

  CssCombiner* combiner() { return &combiner_; }

  bool AddElement(HtmlElement* element, HtmlElement::Attribute* href) {
    bool ret = false;
    ResourcePtr resource(filter_->CreateInputResource(
        href->DecodedValueOrNull()));
    if (resource.get() != NULL) {
      ResourceSlotPtr slot(Driver()->GetSlot(resource, element, href));
      AddSlot(slot);
      elements_.push_back(element);
      ret = true;
    }
    return ret;
  }

  bool empty() const { return elements_.empty(); }
  bool new_combination() const { return new_combination_; }

  void Reset() {
    combiner_.Reset();
    combiner_.set_media("");
    new_combination_ = true;
  }

  void SetMedia(const char* media) {
    combiner_.set_media(media);
    new_combination_ = false;
  }

 protected:
  virtual bool Partition(OutputPartitions* partitions,
                         OutputResourceVector* outputs) {
    MessageHandler* handler = Driver()->message_handler();
    CachedResult* partition = NULL;
    CHECK_EQ(static_cast<int>(elements_.size()), num_slots());
    for (int i = 0, n = num_slots(); i < n; ++i) {
      bool add_input = false;
      ResourcePtr resource(slot(i)->resource());

      if (resource->IsValidAndCacheable()) {
        if (combiner_.AddResourceNoFetch(resource, handler).value) {
          // This new element works in the existing partition.
          add_input = true;
        } else {
          // This new element does not work in the existing partition,
          // so close out that partition if it's non-empty.
          if (partition != NULL) {
            FinalizePartition(partitions, partition, outputs);
            partition = NULL;
            if (combiner_.AddResourceNoFetch(resource, handler).value) {
              add_input = true;
            }
          }
        }
      } else {
        // A failed resource-fetch tells us to finalize any partition that
        // we've already started.  We don't want to combine across a CSS file
        // that our server sees as a 404 because the browser might successfully
        // fetch that file, and thus we'd mangle the ordering if we combined
        // across it.
        FinalizePartition(partitions, partition, outputs);
        partition = NULL;
      }
      if (add_input) {
        if (partition == NULL) {
          partition = partitions->add_partition();
        }
        resource->AddInputInfoToPartition(
            Resource::kIncludeInputHash, i, partition);
      }
    }
    FinalizePartition(partitions, partition, outputs);
    return (partitions->partition_size() != 0);
  }

  virtual void Rewrite(int partition_index,
                       CachedResult* partition,
                       const OutputResourcePtr& output) {
    // resource_combiner.cc calls WriteCombination as part
    // of Combine.  But if we are being called on behalf of a
    // fetch then the resource still needs to be written.
    RewriteResult result = kRewriteOk;
    // OutputResource CHECK-fails if you try to Write twice, which
    // would happen in the html-rewrite phase without this check.
    if (!output->IsWritten()) {
      ResourceVector resources;
      for (int i = 0, n = num_slots(); i < n; ++i) {
        ResourcePtr resource(slot(i)->resource());
        resources.push_back(resource);
      }
      if (!combiner_.Write(resources, output)) {
        result = kRewriteFailed;
      }
    }
    RewriteDone(result, partition_index);
  }

  virtual void Render() {
    // Slot 0 will be replaced by the combined resource as part of
    // rewrite_context.cc.  But we still need to delete slots 1-N.
    for (int p = 0, np = num_output_partitions(); p < np; ++p) {
      CachedResult* partition = output_partition(p);
      if (partition->input_size() == 0) {
        continue;
      }

      if (filter_->driver()->doctype().IsXhtml()) {
        int first_element_index = partition->input(0).index();
        HtmlElement* first_element = elements_[first_element_index];
        first_element->set_close_style(HtmlElement::BRIEF_CLOSE);
      }
      for (int i = 1; i < partition->input_size(); ++i) {
        int slot_index = partition->input(i).index();
        slot(slot_index)->set_should_delete_element(true);
      }
      combiner_.AddFileCountReduction(partition->input_size() - 1);
    }
  }

  virtual const UrlSegmentEncoder* encoder() const {
    return filter_->encoder();
  }
  virtual const char* id() const { return filter_->id(); }
  virtual OutputResourceKind kind() const { return kRewrittenResource; }

 private:
  void FinalizePartition(OutputPartitions* partitions,
                         CachedResult* partition,
                         OutputResourceVector* outputs) {
    if (partition != NULL) {
      OutputResourcePtr combination_output(combiner_.MakeOutput());
      if (combination_output.get() == NULL) {
        partitions->mutable_partition()->RemoveLast();
      } else {
        combination_output->UpdateCachedResultPreservingInputInfo(partition);
        outputs->push_back(combination_output);
      }
      Reset();
    }
  }

  std::vector<HtmlElement*> elements_;
  RewriteFilter* filter_;
  CssCombineFilter::CssCombiner combiner_;
  bool new_combination_;
  DISALLOW_COPY_AND_ASSIGN(Context);
};

// TODO(jmarantz) We exhibit zero intelligence about which css files to
// combine; we combine whatever is possible.  This can reduce performance
// by combining highly cacheable shared resources with transient ones.
//
// TODO(jmarantz): We do not recognize IE directives as spriting boundaries.
// We should supply a meaningful IEDirective method as a boundary.
//
// TODO(jmarantz): allow combining of CSS elements found in the body, whether
// or not the head has already been flushed.
//
// TODO(jmaessen): The addition of 1 below avoids the leading ".";
// make this convention consistent and fix all code.
CssCombineFilter::CssCombineFilter(RewriteDriver* driver)
    : RewriteFilter(driver),
      css_tag_scanner_(driver_) {
}

CssCombineFilter::~CssCombineFilter() {
}

void CssCombineFilter::Initialize(Statistics* statistics) {
  statistics->AddVariable(kCssFileCountReduction);
}

void CssCombineFilter::StartDocumentImpl() {
  context_.reset(MakeContext());
}

void CssCombineFilter::StartElementImpl(HtmlElement* element) {
  HtmlElement::Attribute* href;
  const char* media;
  if (!driver_->HasChildrenInFlushWindow(element) &&
      css_tag_scanner_.ParseCssElement(element, &href, &media)) {
    // We cannot combine with a link in <noscript> tag and we cannot combine
    // over a link in a <noscript> tag, so this is a barrier.
    if (noscript_element() != NULL) {
      NextCombination();
    } else {
      if (context_->new_combination()) {
        context_->SetMedia(media);
      } else if (combiner()->media() != media) {
        // After the first CSS file, subsequent CSS files must have matching
        // media.
        // TODO(jmarantz): do media='' and media='display mean the same
        // thing?  sligocki thinks mdsteele looked into this and it
        // depended on HTML version.  In one display was default, in the
        // other screen was IIRC.
        NextCombination();
        context_->SetMedia(media);
      }
      if (!context_->AddElement(element, href)) {
        NextCombination();
      }
    }
  } else if (element->keyword() == HtmlName::kStyle) {
    // We can't reorder styles on a page, so if we are only combining <link>
    // tags, we can't combine them across a <style> tag.
    // TODO(sligocki): Maybe we should just combine <style>s too?
    // We can run outline_css first for now to make all <style>s into <link>s.
    NextCombination();
  }
}

void CssCombineFilter::NextCombination() {
  if (!context_->empty()) {
    driver_->InitiateRewrite(context_.release());
    context_.reset(MakeContext());
  }
  context_->Reset();
}

// An IE directive that includes any stylesheet info should be a barrier
// for css combining.  It's OK to emit the combination we've seen so far.
void CssCombineFilter::IEDirective(HtmlIEDirectiveNode* directive) {
  // TODO(sligocki): Figure out how to safely parse IEDirectives, for now we
  // just consider them black boxes / solid barriers.
  NextCombination();
}

void CssCombineFilter::Flush() {
  NextCombination();
}

bool CssCombineFilter::CssCombiner::WritePiece(
    int index, const Resource* input, OutputResource* combination,
    Writer* writer, MessageHandler* handler) {
  StringPiece contents = input->contents();
  GoogleUrl input_url(input->url());
  // Strip the BOM off of the contents (if it's there) if this is not the
  // first resource.
  if (index != 0) {
    StripUtf8Bom(&contents);
  }
  bool ret = false;
  switch (rewrite_driver_->ResolveCssUrls(
      input_url, combination->resolved_base(), contents, writer, handler)) {
    case RewriteDriver::kNoResolutionNeeded:
      ret = writer->Write(contents, handler);
      break;
    case RewriteDriver::kWriteFailed:
      break;
    case RewriteDriver::kSuccess:
      ret = true;
      break;
  }
  return ret;
}

CssCombineFilter::CssCombiner* CssCombineFilter::combiner() {
  return context_->combiner();
}

CssCombineFilter::Context* CssCombineFilter::MakeContext() {
  return new Context(driver_, &css_tag_scanner_, this);
}

RewriteContext* CssCombineFilter::MakeRewriteContext() {
  return MakeContext();
}

}  // namespace net_instaweb
<|endoftext|>


<commit_before>#pragma once

#include <cstdint>
#include <iomanip>
#include <sstream>
#include <string>

#include <boost/lexical_cast.hpp>

#include "blackhole/sink/files/rotation/naming.hpp"

namespace blackhole {

namespace sink {

namespace rotation {

struct counter_t {
    std::string prefix;
    std::string suffix;
    uint width;

    counter_t(const std::string& prefix, const std::string& suffix, uint width) :
        prefix(std::move(prefix)),
        suffix(std::move(suffix)),
        width(width)
    {}

    counter_t(std::string&& prefix, std::string&& suffix, uint width) :
        prefix(std::move(prefix)),
        suffix(std::move(suffix)),
        width(width)
    {}

    bool operator ==(const counter_t& other) const {
        return prefix == other.prefix && suffix == other.suffix && width == other.width;
    }

    friend std::ostream& operator <<(std::ostream& stream, const counter_t& counter) {
        stream << "counter_t('" << counter.prefix << "', '" << counter.suffix << "', " << counter.width << ")";
        return stream;
    }

    bool valid() const {
        return width != 0;
    }

    //! I just leave these examples as mini documentation.
    //! pattern:     test.log.%Y%m%d.%N      %(filename).%N      %(filename).%Y%m%d.%N
    //! basename:    test.log.%Y%m%d.%N      test.log.%N         test.log.%Y%m%d.%N
    //! current:     test.log.20140130.1     test.log.1          test.log.20140130.1
    //! newname:     test.log.20140130.2     test.log.2          test.log.20140130.2
    std::string next(const std::string& filename) const {
        BOOST_ASSERT(filename.size() - prefix.size() - suffix.size() >= 0);
        const std::string& counter = filename.substr(prefix.size(),
                                                     filename.size() - prefix.size() - suffix.size());
        uint value = 0;
        try {
            value = boost::lexical_cast<uint>(counter);
        } catch (const boost::bad_lexical_cast&) {
            // Eat this.
        }

        std::ostringstream stream;
        stream << std::string(filename.begin(), filename.begin() + prefix.size())
               << std::setfill('0') << std::setw(width) << (value + 1)
               << std::string(filename.begin() + prefix.size() + std::max(width, matching::digits(value)), filename.end());
        return stream.str();
    }

    static counter_t from_string(const std::string& pattern) {
        std::string prefix;
        std::string suffix;
        int width = 0;
        bool found = false;
        for (auto it = pattern.begin(); it != pattern.end(); ++it) {
            if (found) {
                suffix.push_back(*it);
            } else if (*it == '%') {
                it++;
                std::string value;

                for (; it != pattern.end(); ++it) {
                    if (*it == 'N') {
                        found = true;
                        break;
                    }

                    value.push_back(*it);
                    if (!std::isdigit(*it)) {
                        break;
                    }
                }

                if (found) {
                    width = value.empty() ? 1 : boost::lexical_cast<uint>(value);
                } else {
                    prefix.push_back('%');
                    prefix.append(value);
                }
            } else {
                prefix.push_back(*it);
            }
        }

        boost::replace_all(prefix, "%Y", "YYYY");
        boost::replace_all(prefix, "%m", "mm");
        boost::replace_all(prefix, "%d", "dd");
        boost::replace_all(prefix, "%H", "HH");
        boost::replace_all(prefix, "%M", "MM");
        boost::replace_all(prefix, "%s", "ss");

        boost::replace_all(suffix, "%Y", "YYYY");
        boost::replace_all(suffix, "%m", "mm");
        boost::replace_all(suffix, "%d", "dd");
        boost::replace_all(suffix, "%H", "HH");
        boost::replace_all(suffix, "%M", "MM");
        boost::replace_all(suffix, "%s", "ss");

        return counter_t(std::move(prefix), std::move(suffix), width);
    }

    int count(const std::string& str, const std::string& obj ) {
        int n = 0;
        std::string::size_type pos = 0;
        while ((pos = obj.find(str, pos)) != std::string::npos) {
            n++;
            pos += str.size();
        }

        return n;
    }
};

} // namespace rotation

} // namespace sink

} // namespace blackhole
<commit_msg>Tiny comment fix.<commit_after>#pragma once

#include <cstdint>
#include <iomanip>
#include <sstream>
#include <string>

#include <boost/lexical_cast.hpp>

#include "blackhole/sink/files/rotation/naming.hpp"

namespace blackhole {

namespace sink {

namespace rotation {

struct counter_t {
    std::string prefix;
    std::string suffix;
    uint width;

    counter_t(const std::string& prefix, const std::string& suffix, uint width) :
        prefix(std::move(prefix)),
        suffix(std::move(suffix)),
        width(width)
    {}

    counter_t(std::string&& prefix, std::string&& suffix, uint width) :
        prefix(std::move(prefix)),
        suffix(std::move(suffix)),
        width(width)
    {}

    bool operator ==(const counter_t& other) const {
        return prefix == other.prefix && suffix == other.suffix && width == other.width;
    }

    friend std::ostream& operator <<(std::ostream& stream, const counter_t& counter) {
        stream << "counter_t('" << counter.prefix << "', '" << counter.suffix << "', " << counter.width << ")";
        return stream;
    }

    bool valid() const {
        return width != 0;
    }

    //! I just leave these examples as mini documentation.
    /*!
     * pattern:     test.log.%Y%m%d.%N      %(filename)s.%N     %(filename)s.%Y%m%d.%N
     * basename:    test.log.%Y%m%d.%N      test.log.%N         test.log.%Y%m%d.%N
     * current:     test.log.20140130.1     test.log.1          test.log.20140130.1
     * newname:     test.log.20140130.2     test.log.2          test.log.20140130.2
     */
    std::string next(const std::string& filename) const {
        BOOST_ASSERT(filename.size() - prefix.size() - suffix.size() >= 0);
        const std::string& counter = filename.substr(prefix.size(),
                                                     filename.size() - prefix.size() - suffix.size());
        uint value = 0;
        try {
            value = boost::lexical_cast<uint>(counter);
        } catch (const boost::bad_lexical_cast&) {
            // Eat this.
        }

        std::ostringstream stream;
        stream << std::string(filename.begin(), filename.begin() + prefix.size())
               << std::setfill('0') << std::setw(width) << (value + 1)
               << std::string(filename.begin() + prefix.size() + std::max(width, matching::digits(value)), filename.end());
        return stream.str();
    }

    static counter_t from_string(const std::string& pattern) {
        std::string prefix;
        std::string suffix;
        int width = 0;
        bool found = false;
        for (auto it = pattern.begin(); it != pattern.end(); ++it) {
            if (found) {
                suffix.push_back(*it);
            } else if (*it == '%') {
                it++;
                std::string value;

                for (; it != pattern.end(); ++it) {
                    if (*it == 'N') {
                        found = true;
                        break;
                    }

                    value.push_back(*it);
                    if (!std::isdigit(*it)) {
                        break;
                    }
                }

                if (found) {
                    width = value.empty() ? 1 : boost::lexical_cast<uint>(value);
                } else {
                    prefix.push_back('%');
                    prefix.append(value);
                }
            } else {
                prefix.push_back(*it);
            }
        }

        boost::replace_all(prefix, "%Y", "YYYY");
        boost::replace_all(prefix, "%m", "mm");
        boost::replace_all(prefix, "%d", "dd");
        boost::replace_all(prefix, "%H", "HH");
        boost::replace_all(prefix, "%M", "MM");
        boost::replace_all(prefix, "%s", "ss");

        boost::replace_all(suffix, "%Y", "YYYY");
        boost::replace_all(suffix, "%m", "mm");
        boost::replace_all(suffix, "%d", "dd");
        boost::replace_all(suffix, "%H", "HH");
        boost::replace_all(suffix, "%M", "MM");
        boost::replace_all(suffix, "%s", "ss");

        return counter_t(std::move(prefix), std::move(suffix), width);
    }

    int count(const std::string& str, const std::string& obj ) {
        int n = 0;
        std::string::size_type pos = 0;
        while ((pos = obj.find(str, pos)) != std::string::npos) {
            n++;
            pos += str.size();
        }

        return n;
    }
};

} // namespace rotation

} // namespace sink

} // namespace blackhole
<|endoftext|>


<commit_before>#include "TMC2130Stepper.h"

#define REG_CHOPCONF 0x6C

///////////////////////////////////////////////////////////////////////////////////////
// REG_CHOPCONF

uint32_t TMC2130Stepper::CHOPCONF() {
	uint32_t data = 0x0;
	send2130(READ|REG_CHOPCONF, &data, 0x0, 0x0);
	return data;
}

void TMC2130Stepper::CHOPCONF(uint32_t value) {
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, value, 0xFFFFFFFF);
}

uint8_t TMC2130Stepper::off_time() {return val_toff;}

void TMC2130Stepper::off_time(uint8_t value) {
	if (value > 15) value = 15;
	val_toff = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, value, 0xF);
}

uint8_t TMC2130Stepper::hysterisis_start() {return val_hstrt;}

void TMC2130Stepper::hysterisis_start(uint8_t value) {
	if (val_chm	== 0) {
		if (value < 1) value = 1;
		else if (value > 8) value = 8;
		val_hstrt = value;
		send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (value-1) << 4, 0x70);
	}
#ifdef TMC2130DEBUG
	else {Serial.println("chopper_mode bit is set to 1 -> fast_decay_time is in use. No change made.");}
#endif
}

uint8_t TMC2130Stepper::fast_decay_time() {return val_tfd;}

void TMC2130Stepper::fast_decay_time(uint8_t value) {
	if (val_chm == 1) {
		if (value > 15) value = 15;
		val_tfd = value;
		value = ((uint32_t)value << 4) | (value & 0b111); // Create space between MSB and the bits 0..2
		send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 4, 0x870);
	}
#ifdef TMC2130DEBUG
	else {Serial.println("chopper_mode bit is set to 0 -> hysterisis_start is in use. No change made.");}
#endif
}

int8_t TMC2130Stepper::hysterisis_low() {return val_hend;}

void TMC2130Stepper::hysterisis_low(int8_t value) {
	if (val_chm == 0) {
		if (value < -3) value = -3;
		if (value > 12) value = 12;
		val_hend = value;
		send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (value+3) << 7, 0x780);
	}
#ifdef TMC2130DEBUG
	else {Serial.println("chopper_mode bit is set to 1 -> sine_offset is in use. No change made.");}
#endif
}

int8_t TMC2130Stepper::sine_offset() {return val_offset;}

void TMC2130Stepper::sine_offset(int8_t value) {
	if (val_chm == 1) {
		if (value < -3) value = -3;
		if (value > 12) value = 12;
		val_hend = value;
		send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (value+3) << 7, 0x780);
	}
#ifdef TMC2130DEBUG
	else {Serial.println("chopper_mode bit is set to 0 -> hysterisis_low is in use. No change made.");}
#endif
}

uint8_t TMC2130Stepper::disable_I_comparator() {return val_disfdcc;}

void TMC2130Stepper::disable_I_comparator(uint8_t value) {
	if (val_chm == 1) {
		if (value > 1) value = 1;
		val_disfdcc = value;
		send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 12, (uint32_t)0b1 << 12);
	}
#ifdef TMC2130DEBUG
	else {Serial.println("chopper_mode bit is set to 0 -> No change made.");}
#endif
}

uint8_t TMC2130Stepper::random_off_time() {return val_rndtf;}

void TMC2130Stepper::random_off_time(uint8_t value) {
	if (value > 1) value = 1;
	val_rndtf = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 13, (uint32_t)0b1 << 13);
}

uint8_t TMC2130Stepper::chopper_mode() {return val_chm;}

void TMC2130Stepper::chopper_mode(uint8_t value) {
	if (value > 1) value = 1;
	val_chm = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 14, (uint32_t)0b1 << 14);
}

uint8_t TMC2130Stepper::blank_time() {return val_tbl;}

void TMC2130Stepper::blank_time(uint8_t value) {
	uint8_t valid[] = {54, 36, 24, 16};

	if (value < valid[3]) value = valid[3]; // Make sure we find a match for low values
	for (int i = 0; i<4; i++) {
		if (value >= valid[i]) {
			value = valid[i];
			break;
		}
	}

	val_tbl = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 15, 0x18000);
}

uint8_t TMC2130Stepper::high_sense_R() {return val_vsense;}

void TMC2130Stepper::high_sense_R(uint8_t value) {
	if (value > 1) value = 1;
	val_vsense = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)(uint32_t)value << 17, (uint32_t)0b1 << 17);
}

uint8_t TMC2130Stepper::fullstep_threshold() {return val_vhighfs;}

void TMC2130Stepper::fullstep_threshold(uint8_t value) {
	if (value > 1) value = 1;
	val_vhighfs = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)(uint32_t)value << 18, (uint32_t)0b1 << 18);
}

uint8_t TMC2130Stepper::high_speed_mode() {return val_vhighchm;}

void TMC2130Stepper::high_speed_mode(uint8_t value) {
	if (value > 1) value = 1;
	val_vhighchm = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)(uint32_t)value << 19, (uint32_t)0b1 << 19);
}

uint8_t TMC2130Stepper::sync_phases() {return val_sync;}

void TMC2130Stepper::sync_phases(uint8_t value) {
	if (value > 15) value = 15;
	val_sync = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)(uint32_t)value << 20, 0xF00000);
}

uint8_t TMC2130Stepper::microsteps() {return val_mres;}

void TMC2130Stepper::microsteps(uint8_t value) {
	int valid[] 	= {		256, 	128, 	 64, 	 32, 	 16, 	  8, 	  4, 	  2, 	  0 };
	uint32_t _hex[] = {  0b0000, 0b0001, 0b0010, 0b0011, 0b0100, 0b0101, 0b0110, 0b0111, 0b1000 };

	for (int i = 0; i<9; i++) {
		if (value >= valid[i]) {
			val_mres = valid[i];
			send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, _hex[i] << 24, 0xF000000);
			break;
		}
	}
}

uint8_t TMC2130Stepper::interpolate() {return val_intpol;}

void TMC2130Stepper::interpolate(uint8_t value) {
	if (value > 1) value = 1;
	val_intpol = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)(uint32_t)value << 28, (uint32_t)0b1 << 28);
}

uint8_t TMC2130Stepper::double_edge_step() {return val_dedge;}

void TMC2130Stepper::double_edge_step(uint8_t value) {
	if (value > 1) value = 1;
	val_dedge = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)(uint32_t)value << 29, (uint32_t)0b1 << 29);
}

uint8_t TMC2130Stepper::disable_short_protection() {return val_diss2g;}

void TMC2130Stepper::disable_short_protection(uint8_t value) {
	if (value > 1) value = 1;
	val_diss2g = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)(uint32_t)value << 30, (uint32_t)0b1 << 30);
}<commit_msg>Fixed double casts<commit_after>#include "TMC2130Stepper.h"

#define REG_CHOPCONF 0x6C

///////////////////////////////////////////////////////////////////////////////////////
// REG_CHOPCONF

uint32_t TMC2130Stepper::CHOPCONF() {
	uint32_t data = 0x0;
	send2130(READ|REG_CHOPCONF, &data, 0x0, 0x0);
	return data;
}

void TMC2130Stepper::CHOPCONF(uint32_t value) {
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, value, 0xFFFFFFFF);
}

uint8_t TMC2130Stepper::off_time() {return val_toff;}

void TMC2130Stepper::off_time(uint8_t value) {
	if (value > 15) value = 15;
	val_toff = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, value, 0xF);
}

uint8_t TMC2130Stepper::hysterisis_start() {return val_hstrt;}

void TMC2130Stepper::hysterisis_start(uint8_t value) {
	if (val_chm	== 0) {
		if (value < 1) value = 1;
		else if (value > 8) value = 8;
		val_hstrt = value;
		send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (value-1) << 4, 0x70);
	}
#ifdef TMC2130DEBUG
	else {Serial.println("chopper_mode bit is set to 1 -> fast_decay_time is in use. No change made.");}
#endif
}

uint8_t TMC2130Stepper::fast_decay_time() {return val_tfd;}

void TMC2130Stepper::fast_decay_time(uint8_t value) {
	if (val_chm == 1) {
		if (value > 15) value = 15;
		val_tfd = value;
		value = ((uint32_t)value << 4) | (value & 0b111); // Create space between MSB and the bits 0..2
		send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 4, 0x870);
	}
#ifdef TMC2130DEBUG
	else {Serial.println("chopper_mode bit is set to 0 -> hysterisis_start is in use. No change made.");}
#endif
}

int8_t TMC2130Stepper::hysterisis_low() {return val_hend;}

void TMC2130Stepper::hysterisis_low(int8_t value) {
	if (val_chm == 0) {
		if (value < -3) value = -3;
		if (value > 12) value = 12;
		val_hend = value;
		send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (value+3) << 7, 0x780);
	}
#ifdef TMC2130DEBUG
	else {Serial.println("chopper_mode bit is set to 1 -> sine_offset is in use. No change made.");}
#endif
}

int8_t TMC2130Stepper::sine_offset() {return val_offset;}

void TMC2130Stepper::sine_offset(int8_t value) {
	if (val_chm == 1) {
		if (value < -3) value = -3;
		if (value > 12) value = 12;
		val_hend = value;
		send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (value+3) << 7, 0x780);
	}
#ifdef TMC2130DEBUG
	else {Serial.println("chopper_mode bit is set to 0 -> hysterisis_low is in use. No change made.");}
#endif
}

uint8_t TMC2130Stepper::disable_I_comparator() {return val_disfdcc;}

void TMC2130Stepper::disable_I_comparator(uint8_t value) {
	if (val_chm == 1) {
		if (value > 1) value = 1;
		val_disfdcc = value;
		send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 12, (uint32_t)0b1 << 12);
	}
#ifdef TMC2130DEBUG
	else {Serial.println("chopper_mode bit is set to 0 -> No change made.");}
#endif
}

uint8_t TMC2130Stepper::random_off_time() {return val_rndtf;}

void TMC2130Stepper::random_off_time(uint8_t value) {
	if (value > 1) value = 1;
	val_rndtf = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 13, (uint32_t)0b1 << 13);
}

uint8_t TMC2130Stepper::chopper_mode() {return val_chm;}

void TMC2130Stepper::chopper_mode(uint8_t value) {
	if (value > 1) value = 1;
	val_chm = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 14, (uint32_t)0b1 << 14);
}

uint8_t TMC2130Stepper::blank_time() {return val_tbl;}

void TMC2130Stepper::blank_time(uint8_t value) {
	uint8_t valid[] = {54, 36, 24, 16};

	if (value < valid[3]) value = valid[3]; // Make sure we find a match for low values
	for (int i = 0; i<4; i++) {
		if (value >= valid[i]) {
			value = valid[i];
			break;
		}
	}

	val_tbl = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 15, 0x18000);
}

uint8_t TMC2130Stepper::high_sense_R() {return val_vsense;}

void TMC2130Stepper::high_sense_R(uint8_t value) {
	if (value > 1) value = 1;
	val_vsense = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 17, (uint32_t)0b1 << 17);
}

uint8_t TMC2130Stepper::fullstep_threshold() {return val_vhighfs;}

void TMC2130Stepper::fullstep_threshold(uint8_t value) {
	if (value > 1) value = 1;
	val_vhighfs = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 18, (uint32_t)0b1 << 18);
}

uint8_t TMC2130Stepper::high_speed_mode() {return val_vhighchm;}

void TMC2130Stepper::high_speed_mode(uint8_t value) {
	if (value > 1) value = 1;
	val_vhighchm = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 19, (uint32_t)0b1 << 19);
}

uint8_t TMC2130Stepper::sync_phases() {return val_sync;}

void TMC2130Stepper::sync_phases(uint8_t value) {
	if (value > 15) value = 15;
	val_sync = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 20, 0xF00000);
}

uint8_t TMC2130Stepper::microsteps() {return val_mres;}

void TMC2130Stepper::microsteps(uint8_t value) {
	int valid[] 	= {		256, 	128, 	 64, 	 32, 	 16, 	  8, 	  4, 	  2, 	  0 };
	uint32_t _hex[] = {  0b0000, 0b0001, 0b0010, 0b0011, 0b0100, 0b0101, 0b0110, 0b0111, 0b1000 };

	for (int i = 0; i<9; i++) {
		if (value >= valid[i]) {
			val_mres = valid[i];
			send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, _hex[i] << 24, 0xF000000);
			break;
		}
	}
}

uint8_t TMC2130Stepper::interpolate() {return val_intpol;}

void TMC2130Stepper::interpolate(uint8_t value) {
	if (value > 1) value = 1;
	val_intpol = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 28, (uint32_t)0b1 << 28);
}

uint8_t TMC2130Stepper::double_edge_step() {return val_dedge;}

void TMC2130Stepper::double_edge_step(uint8_t value) {
	if (value > 1) value = 1;
	val_dedge = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 29, (uint32_t)0b1 << 29);
}

uint8_t TMC2130Stepper::disable_short_protection() {return val_diss2g;}

void TMC2130Stepper::disable_short_protection(uint8_t value) {
	if (value > 1) value = 1;
	val_diss2g = value;
	send2130(WRITE|REG_CHOPCONF, &cur_CHOPCONF, (uint32_t)value << 30, (uint32_t)0b1 << 30);
}<|endoftext|>


<commit_before>#ifndef __RANK_FILTER__
#define __RANK_FILTER__

#include <deque>
#include <cassert>
#include <functional>
#include <iostream>
#include <iterator>

#include <boost/array.hpp>
#include <boost/container/set.hpp>
#include <boost/container/node_allocator.hpp>
#include <boost/math/special_functions/round.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>

namespace rank_filter
{

template<class I1,
         class I2>
inline void lineRankOrderFilter1D(const I1& src_begin, const I1& src_end,
        I2& dest_begin, I2& dest_end,
        size_t half_length, double rank)
{
    // Types in use.
    typedef typename std::iterator_traits<I1>::value_type T1;
    typedef typename std::iterator_traits<I2>::value_type T2;
    typedef typename std::iterator_traits<I1>::difference_type I1_diff_t;
    typedef typename std::iterator_traits<I2>::difference_type I2_diff_t;

    // Establish common types to work with source and destination values.
    BOOST_STATIC_ASSERT((boost::is_same<T1, T2>::value));
    typedef T1 T;
    typedef typename boost::common_type<I1_diff_t, I2_diff_t>::type I_diff_t;

    // Define container types that will be used.
    typedef boost::container::multiset< T,
            std::less<T>,
            boost::container::node_allocator<T>,
            boost::container::tree_assoc_options< boost::container::tree_type<boost::container::scapegoat_tree> >::type> multiset;
    typedef std::deque< typename multiset::iterator > deque;

    // Lengths.
    const I_diff_t src_size = std::distance(src_begin, src_end);
    const I_diff_t dest_size = std::distance(dest_begin, dest_end);

    // Ensure the result will fit.
    assert(src_size <= dest_size);

    // Window length cannot exceed input data with reflection.
    assert((half_length + 1) <= src_size);

    // Rank must be in the range 0 to 1.
    assert((0 <= rank) && (rank <= 1));

    // The position of the window.
    I_diff_t window_begin = 0;

    // Window position corresponding to this rank.
    const I_diff_t rank_pos;
    typename multiset::iterator rank_point;

    // Track values in window both in sorted and sequential order.
    multiset sorted_window;
    deque window_iters(2 * half_length + 1);

    // Get the initial sorted window.
    // Include the reflection.
    for (I_diff_t j = 0; j < half_length; j++)
    {
        window_iters[j] = sorted_window.insert(src_begin[window_begin + half_length - j]);
    }
    for (I_diff_t j = half_length; j < (2 * half_length + 1); j++)
    {
        window_iters[j] = sorted_window.insert(src_begin[window_begin + j - half_length]);
    }

    rank_pos = static_cast<I_diff_t>(boost::math::round(rank * (2 * half_length)));
    rank_point = sorted_window.begin();
    for (I_diff_t i = 0; i < rank_pos; i++)
    {
        rank_point++;
    }

    typename multiset::iterator prev_iter;
    T prev_value;
    T next_value;
    while ( window_begin < src_size )
    {
        dest_begin[window_begin] = *rank_point;

        prev_iter = window_iters.front();
        prev_value = *prev_iter;
        window_iters.pop_front();

        window_begin++;

        if ( window_begin == src_size )
        {
            next_value = prev_value;
        }
        else if ( window_begin < (src_size - half_length) )
        {
            next_value = src_begin[window_begin + half_length];
        }
        else
        {
            next_value = *(window_iters[(2 * (src_size - (window_begin + 1)))]);
        }

        if ( ( *rank_point < prev_value ) && ( *rank_point <= next_value ) )
        {
            sorted_window.erase(prev_iter);
            window_iters.push_back(sorted_window.insert(next_value));
        }
        else if ( ( *rank_point >= prev_value ) && ( *rank_point > next_value ) )
        {
            if ( rank_point == prev_iter )
            {
                window_iters.push_back(sorted_window.insert(next_value));
                rank_point--;

                sorted_window.erase(prev_iter);
            }
            else
            {
                sorted_window.erase(prev_iter);
                window_iters.push_back(sorted_window.insert(next_value));
            }
        }
        else if ( ( *rank_point < prev_value ) && ( *rank_point > next_value ) )
        {
            sorted_window.erase(prev_iter);
            window_iters.push_back(sorted_window.insert(next_value));

            rank_point--;
        }
        else if ( ( *rank_point >= prev_value ) && ( *rank_point <= next_value ) )
        {
            if (rank_point == prev_iter)
            {
                window_iters.push_back(sorted_window.insert(next_value));
                rank_point++;

                sorted_window.erase(prev_iter);
            }
            else
            {
                sorted_window.erase(prev_iter);
                window_iters.push_back(sorted_window.insert(next_value));

                rank_point++;
            }
        }
    }
}

}

namespace std
{
    template <class T, size_t N>
    ostream& operator<<(ostream& out, const boost::array<T, N>& that)
    {
        out << "{ ";
        for (unsigned int i = 0; i < (N - 1); i++)
        {
            out << that[i] << ", ";
        }
        out << that[N - 1] << " }";

        return(out);
    }
}

#endif //__RANK_FILTER__
<commit_msg>Explain initialization of `rank_point`<commit_after>#ifndef __RANK_FILTER__
#define __RANK_FILTER__

#include <deque>
#include <cassert>
#include <functional>
#include <iostream>
#include <iterator>

#include <boost/array.hpp>
#include <boost/container/set.hpp>
#include <boost/container/node_allocator.hpp>
#include <boost/math/special_functions/round.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>

namespace rank_filter
{

template<class I1,
         class I2>
inline void lineRankOrderFilter1D(const I1& src_begin, const I1& src_end,
        I2& dest_begin, I2& dest_end,
        size_t half_length, double rank)
{
    // Types in use.
    typedef typename std::iterator_traits<I1>::value_type T1;
    typedef typename std::iterator_traits<I2>::value_type T2;
    typedef typename std::iterator_traits<I1>::difference_type I1_diff_t;
    typedef typename std::iterator_traits<I2>::difference_type I2_diff_t;

    // Establish common types to work with source and destination values.
    BOOST_STATIC_ASSERT((boost::is_same<T1, T2>::value));
    typedef T1 T;
    typedef typename boost::common_type<I1_diff_t, I2_diff_t>::type I_diff_t;

    // Define container types that will be used.
    typedef boost::container::multiset< T,
            std::less<T>,
            boost::container::node_allocator<T>,
            boost::container::tree_assoc_options< boost::container::tree_type<boost::container::scapegoat_tree> >::type> multiset;
    typedef std::deque< typename multiset::iterator > deque;

    // Lengths.
    const I_diff_t src_size = std::distance(src_begin, src_end);
    const I_diff_t dest_size = std::distance(dest_begin, dest_end);

    // Ensure the result will fit.
    assert(src_size <= dest_size);

    // Window length cannot exceed input data with reflection.
    assert((half_length + 1) <= src_size);

    // Rank must be in the range 0 to 1.
    assert((0 <= rank) && (rank <= 1));

    // The position of the window.
    I_diff_t window_begin = 0;

    // Window position corresponding to this rank.
    const I_diff_t rank_pos;
    typename multiset::iterator rank_point;

    // Track values in window both in sorted and sequential order.
    multiset sorted_window;
    deque window_iters(2 * half_length + 1);

    // Get the initial sorted window.
    // Include the reflection.
    for (I_diff_t j = 0; j < half_length; j++)
    {
        window_iters[j] = sorted_window.insert(src_begin[window_begin + half_length - j]);
    }
    for (I_diff_t j = half_length; j < (2 * half_length + 1); j++)
    {
        window_iters[j] = sorted_window.insert(src_begin[window_begin + j - half_length]);
    }

    // Move our selection point to the corresponding rank.
    rank_pos = static_cast<I_diff_t>(boost::math::round(rank * (2 * half_length)));
    rank_point = sorted_window.begin();
    for (I_diff_t i = 0; i < rank_pos; i++)
    {
        rank_point++;
    }

    typename multiset::iterator prev_iter;
    T prev_value;
    T next_value;
    while ( window_begin < src_size )
    {
        dest_begin[window_begin] = *rank_point;

        prev_iter = window_iters.front();
        prev_value = *prev_iter;
        window_iters.pop_front();

        window_begin++;

        if ( window_begin == src_size )
        {
            next_value = prev_value;
        }
        else if ( window_begin < (src_size - half_length) )
        {
            next_value = src_begin[window_begin + half_length];
        }
        else
        {
            next_value = *(window_iters[(2 * (src_size - (window_begin + 1)))]);
        }

        if ( ( *rank_point < prev_value ) && ( *rank_point <= next_value ) )
        {
            sorted_window.erase(prev_iter);
            window_iters.push_back(sorted_window.insert(next_value));
        }
        else if ( ( *rank_point >= prev_value ) && ( *rank_point > next_value ) )
        {
            if ( rank_point == prev_iter )
            {
                window_iters.push_back(sorted_window.insert(next_value));
                rank_point--;

                sorted_window.erase(prev_iter);
            }
            else
            {
                sorted_window.erase(prev_iter);
                window_iters.push_back(sorted_window.insert(next_value));
            }
        }
        else if ( ( *rank_point < prev_value ) && ( *rank_point > next_value ) )
        {
            sorted_window.erase(prev_iter);
            window_iters.push_back(sorted_window.insert(next_value));

            rank_point--;
        }
        else if ( ( *rank_point >= prev_value ) && ( *rank_point <= next_value ) )
        {
            if (rank_point == prev_iter)
            {
                window_iters.push_back(sorted_window.insert(next_value));
                rank_point++;

                sorted_window.erase(prev_iter);
            }
            else
            {
                sorted_window.erase(prev_iter);
                window_iters.push_back(sorted_window.insert(next_value));

                rank_point++;
            }
        }
    }
}

}

namespace std
{
    template <class T, size_t N>
    ostream& operator<<(ostream& out, const boost::array<T, N>& that)
    {
        out << "{ ";
        for (unsigned int i = 0; i < (N - 1); i++)
        {
            out << that[i] << ", ";
        }
        out << that[N - 1] << " }";

        return(out);
    }
}

#endif //__RANK_FILTER__
<|endoftext|>


<commit_before>// cg.cpp: multi-precision, preconditioned Conjugate Gradient iterative solver
//
// Copyright (C) 2017-2020 Stillwater Supercomputing, Inc.
// Authors: Theodore Omtzigt
//
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
#ifdef _MSC_VER
#pragma warning(disable : 4514)   // unreferenced inline function has been removed
#pragma warning(disable : 4710)   // 'int sprintf_s(char *const ,const size_t,const char *const ,...)': function not inlined
#pragma warning(disable : 4820)   // 'sw::unum::value<23>': '3' bytes padding added after data member 'sw::unum::value<23>::_sign'
#pragma warning(disable : 5045)   // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
#endif

// standard library
#include <limits>
// Configure the posit library with arithmetic exceptions
// enable posit arithmetic exceptions
#define POSIT_THROW_ARITHMETIC_EXCEPTION 1
#include <universal/posit/posit>
#include <universal/blas/blas.hpp>
#include <universal/blas/generators.hpp>

// cg: Solution of x in Ax=b using preconditioned Conjugate Gradient algorithm
// with different precision for matrix-vector multiply and residual calculation
// Input:
//   preconditioner M
//   system matrix A
//   right hand side b
//   accuracy tolerance for target solution
// Output:
//   number of iterations to reach required accuracy
template<typename Matrix, typename Vector, size_t MAX_ITERATIONS = 10>
size_t cg(const Matrix& M, const Matrix& A, Vector& b, typename Matrix::value_type tolerance = typename Matrix::value_type(0.00001)) {
	using Scalar = typename Matrix::value_type;
	Scalar residual = Scalar(std::numeric_limits<Scalar>::max());
	//size_t m = num_rows(A);
	//size_t n = num_cols(A);
	Vector x(size(b));
	Vector rho(size(b));
	Vector zeta(size(b));
	Vector p(size(b));
	Vector q(size(b));
	Scalar sigma_1{ 0 }, sigma_2{ 0 }, alpha{ 0 }, beta{ 0 };
	rho = b;  // term is b - A * x, but if we use x(0) = 0 vector, rho = b is equivalent
	size_t itr = 0;
	bool firstIteration = true;
	while (residual > tolerance && itr < MAX_ITERATIONS) {
		zeta = sw::unum::blas::solve(M, rho);
		sigma_2 = sigma_1;
		sigma_1 = dot(zeta, rho); // dot product, fused dot product if Scalar is a posit type
		if (firstIteration) {
			firstIteration = false;
			p = zeta;
		}
		else {
			beta = sigma_1 / sigma_2;
			p = zeta + beta * p;
		}
		q = A * p;
		alpha = sigma_1 / dot(p, q);
		Vector x_1(x);
		x = x + alpha * p;
		rho = rho - alpha * q;
		// check for convergence of the system
		residual = norm1(x_1 - x);
		std::cout << '[' << itr << "] " << x << " residual " << residual << std::endl;
		++itr;
	}
	std::cout << "solution in " << itr << " iterations\n";
	std::cout << "solution is " << x << '\n';
	std::cout << "final residual is " << residual << '\n';
	std::cout << "validation\n" << A * x << " = " << b << std::endl;
	return itr;
}

int main(int argc, char** argv)
try {
	using namespace std;
	using namespace sw::unum;
	using namespace sw::unum::blas;

	constexpr size_t nbits = 32;
	constexpr size_t es = 2;
	using Scalar = posit<nbits, es>;
//	using Scalar = float;
	using Matrix = sw::unum::blas::matrix<Scalar>;
	using Vector = sw::unum::blas::vector<Scalar>;

	if (argc == 1) cout << argv[0] << '\n';
	int nrOfFailedTestCases = 0;

	// Initialize 'A', preconditioner 'M', 'b' & intial guess 'x' * _
	constexpr size_t DoF = 8;
	Matrix A;
	tridiag(A, DoF);  // this does a resize of A
	Matrix M = eye<Scalar>(DoF);
	Vector b(DoF);
	Vector x(DoF);
	x = Scalar(1);
	b = A * x;

	cout << A << endl;
	cout << M << endl;
	cout << b << endl;
	constexpr size_t MAX_ITERATIONS = 10;
	size_t itr = cg<Matrix, Vector, MAX_ITERATIONS>(M, A, b);

	if (itr == MAX_ITERATIONS) {
		std::cerr << "Solution failed to converge\n";
		++nrOfFailedTestCases;
	}

	return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
catch (char const* msg) {
	std::cerr << msg << std::endl;
	return EXIT_FAILURE;
}
catch (const posit_arithmetic_exception& err) {
	std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (const quire_exception& err) {
	std::cerr << "Uncaught quire exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (const posit_internal_exception& err) {
	std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (const std::runtime_error& err) {
	std::cerr << "Uncaught runtime exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (...) {
	std::cerr << "Caught unknown exception" << std::endl;
	return EXIT_FAILURE;
}
<commit_msg>adding an LU preconditioner to the mix: and results get worse....<commit_after>// cg.cpp: multi-precision, preconditioned Conjugate Gradient iterative solver
//
// Copyright (C) 2017-2020 Stillwater Supercomputing, Inc.
// Authors: Theodore Omtzigt
//
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
#ifdef _MSC_VER
#pragma warning(disable : 4514)   // unreferenced inline function has been removed
#pragma warning(disable : 4710)   // 'int sprintf_s(char *const ,const size_t,const char *const ,...)': function not inlined
#pragma warning(disable : 4820)   // 'sw::unum::value<23>': '3' bytes padding added after data member 'sw::unum::value<23>::_sign'
#pragma warning(disable : 5045)   // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
#endif

// standard library
#include <limits>
// Configure the posit library with arithmetic exceptions
// enable posit arithmetic exceptions
#define POSIT_THROW_ARITHMETIC_EXCEPTION 1
#include <universal/posit/posit>
#include <universal/blas/blas.hpp>
#include <universal/blas/generators.hpp>

// cg: Solution of x in Ax=b using preconditioned Conjugate Gradient algorithm
// with different precision for matrix-vector multiply and residual calculation
// Input:
//   preconditioner M
//   system matrix A
//   right hand side b
//   accuracy tolerance for target solution
// Output:
//   number of iterations to reach required accuracy
template<typename Matrix, typename Vector, size_t MAX_ITERATIONS = 10>
size_t cg(const Matrix& M, const Matrix& A, Vector& b, typename Matrix::value_type tolerance = typename Matrix::value_type(0.00001)) {
	using Scalar = typename Matrix::value_type;
	Scalar residual = Scalar(std::numeric_limits<Scalar>::max());
	//size_t m = num_rows(A);
	//size_t n = num_cols(A);
	Vector x(size(b));
	Vector rho(size(b));
	Vector zeta(size(b));
	Vector p(size(b));
	Vector q(size(b));
	Scalar sigma_1{ 0 }, sigma_2{ 0 }, alpha{ 0 }, beta{ 0 };
	rho = b;  // term is b - A * x, but if we use x(0) = 0 vector, rho = b is equivalent
	size_t itr = 0;
	bool firstIteration = true;
	while (residual > tolerance && itr < MAX_ITERATIONS) {
		zeta = sw::unum::blas::solve(M, rho);
		sigma_2 = sigma_1;
		sigma_1 = dot(zeta, rho); // dot product, fused dot product if Scalar is a posit type
		if (firstIteration) {
			firstIteration = false;
			p = zeta;
		}
		else {
			beta = sigma_1 / sigma_2;
			p = zeta + beta * p;
		}
		q = A * p;
		alpha = sigma_1 / dot(p, q);
		Vector x_1(x);
		x = x + alpha * p;
		rho = rho - alpha * q;
		// check for convergence of the system
		residual = norm1(x_1 - x);
		std::cout << '[' << itr << "] " << std::setw(12) << x << " residual " << residual << std::endl;
		++itr;
	}
	std::cout << "solution in " << itr << " iterations\n";
	std::cout << "solution is " << x << '\n';
	std::cout << "final residual is " << residual << '\n';
	std::cout << "validation\n" << A * x << " = " << b << std::endl;
	return itr;
}

int main(int argc, char** argv)
try {
	using namespace std;
	using namespace sw::unum;
	using namespace sw::unum::blas;

	constexpr size_t nbits = 32;
	constexpr size_t es = 2;
	using Scalar = posit<nbits, es>;
//	using Scalar = float;
	using Matrix = sw::unum::blas::matrix<Scalar>;
	using Vector = sw::unum::blas::vector<Scalar>;

	if (argc == 1) cout << argv[0] << '\n';
	int nrOfFailedTestCases = 0;

	// Initialize 'A', preconditioner 'M', 'b' & intial guess 'x' * _
	constexpr size_t DoF = 8;
	Matrix A;
	tridiag(A, DoF);  // this does a resize of A
	Matrix M = eye<Scalar>(DoF);
	Vector b(DoF);
	Vector x(DoF);
	x = Scalar(1);
	b = A * x;

	cout << A << endl;
	cout << M << endl;
	cout << b << endl;
	constexpr size_t MAX_ITERATIONS = 10;
	size_t itr = cg<Matrix, Vector, MAX_ITERATIONS>(M, A, b);

	if (itr == MAX_ITERATIONS) {
		std::cerr << "Solution failed to converge\n";
		++nrOfFailedTestCases;
	}

	M = sw::unum::blas::inv(A);
	itr = cg<Matrix, Vector, MAX_ITERATIONS>(M, A, b);

	if (itr == MAX_ITERATIONS) {
		std::cerr << "Solution failed to converge\n";
		++nrOfFailedTestCases;
	}
	return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
catch (char const* msg) {
	std::cerr << msg << std::endl;
	return EXIT_FAILURE;
}
catch (const posit_arithmetic_exception& err) {
	std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (const quire_exception& err) {
	std::cerr << "Uncaught quire exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (const posit_internal_exception& err) {
	std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (const std::runtime_error& err) {
	std::cerr << "Uncaught runtime exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (...) {
	std::cerr << "Caught unknown exception" << std::endl;
	return EXIT_FAILURE;
}
<|endoftext|>


<commit_before>#include "FloatProperty.h"
#include "ExposureProperty.h"
#include "CallBackManager.h"
#include <cmath>
#include <iomanip>

using namespace andor;
using namespace std;

TExposureProperty::TExposureProperty(const string & MM_name, IFloat * float_feature, ICallBackManager* callback,
                                bool readOnly, bool needsCallBack)
: MM_name_(MM_name),
  float_feature_(float_feature),
  callback_(callback),
  callbackRegistered_(needsCallBack)
{
   if (float_feature->IsImplemented())
   {
      CPropertyAction * pAct = new CPropertyAction (this, &TExposureProperty::OnFloat);
      callback->CPCCreateProperty(MM_name.c_str(), "", MM::Float, readOnly, pAct);

      try
      {
         if (needsCallBack)
         {
            float_feature_->Attach(this);
         }
      }
      catch (exception & e)
      {
         // SDK3 Callback not implemented for this feature
         callback->CPCLog(e.what());
      }
   }
   else
   {
      callbackRegistered_ = false;
   }
}

TExposureProperty::~TExposureProperty()
{
   if (callbackRegistered_)
   {
      try
      {
         float_feature_->Detach(this);
      }
      catch (exception & e)
      {
         // SDK3 Callback not implemented for this feature
         callback_->CPCLog(e.what());
      }
   }
   //Clean up memory, created as passed in
   callback_->GetCameraDevice()->Release(float_feature_);
}

void TExposureProperty::Update(ISubject * Subject)
{
   //if NOT Poised,... (Snapshot sets this first, then changes trigger silently
   // so once updates get applied, and repoise, snapshot sets true, so no erroneous updates get applied
   if ( !callback_->IsSSCPoised() )
   {
      IFloat * featureSubject = dynamic_cast<IFloat *>(Subject);
      TAndorFloatCache * cache = dynamic_cast<TAndorFloatCache *>(float_feature_);
      if (cache && featureSubject)
      {
         cache->SetCache(featureSubject->Get());
      }
   }
}

void TExposureProperty::setFeatureWithinLimits(double new_value)
{
   try
   {
      if (new_value < float_feature_->Min())
      {
         new_value = float_feature_->Min();
      }
      else if (new_value > float_feature_->Max())
      {
         new_value = float_feature_->Max();
      }
      float_feature_->Set(new_value);
   }
   catch (exception & e)
   {
      callback_->CPCLog(e.what());
   }
}

bool TExposureProperty::valueIsWithinLimits(double new_value)
{
   try
   {
      return new_value > float_feature_->Min() && new_value < float_feature_->Max();
   }
   catch (exception & e)
   {
      callback_->CPCLog(e.what());
      return false;
   }
}

inline bool almostEqual(double val1, double val2, int precisionFactor)
{
   const double base = 10.0;
   double precisionError = 1.0 / pow(base, precisionFactor);
   // Check if val1 and val2 are within precision decimal places
   return ( val1 > (val2 - precisionError) && val1 < (val2 + precisionError)) ? true : false;
}

int TExposureProperty::OnFloat(MM::PropertyBase * pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      TAndorFloatCache * cache = dynamic_cast<TAndorFloatCache *>(float_feature_);
      if (cache)
      {
         pProp->Set(cache->Get(callback_->IsSSCPoised()));
      }
      else
      {
         pProp->Set(float_feature_->Get());
      }

   }
   else if (eAct == MM::AfterSet)
   {
      double new_value = 0.0, current_value = float_feature_->Get();
      pProp->Get(new_value);
      TAndorFloatCache * cache = dynamic_cast<TAndorFloatCache *>(float_feature_);
      if (cache)
      {
         current_value = cache->Get(callback_->IsSSCPoised());
      }
      if (!almostEqual(new_value, current_value, DEC_PLACES_ERROR))
      {
         if(valueIsWithinLimits(new_value))
         {
           setFeatureWithinLimits(new_value);
         }
         else
         {
            bool wasPoised = callback_->IsSSCPoised();

            if(wasPoised)
              callback_->SSCLeavePoised();

            setFeatureWithinLimits(new_value);

            if(wasPoised)
              callback_->SSCEnterPoised();
         }
      }
   }

   return DEVICE_OK;
}

<commit_msg>Fix for fast exposure switching - it did not check iswritable, so sometimes the user's value would not be set. Now, if the exposureTime is not writable the current acquisition will be stopped, and restarted after the change. The problem was only re-created using a 4.2 10-tap Zyla with overlap off.<commit_after>#include "FloatProperty.h"
#include "ExposureProperty.h"
#include "CallBackManager.h"
#include <cmath>
#include <iomanip>

using namespace andor;
using namespace std;

TExposureProperty::TExposureProperty(const string & MM_name, IFloat * float_feature, ICallBackManager* callback,
                                bool readOnly, bool needsCallBack)
: MM_name_(MM_name),
  float_feature_(float_feature),
  callback_(callback),
  callbackRegistered_(needsCallBack)
{
   if (float_feature->IsImplemented())
   {
      CPropertyAction * pAct = new CPropertyAction (this, &TExposureProperty::OnFloat);
      callback->CPCCreateProperty(MM_name.c_str(), "", MM::Float, readOnly, pAct);

      try
      {
         if (needsCallBack)
         {
            float_feature_->Attach(this);
         }
      }
      catch (exception & e)
      {
         // SDK3 Callback not implemented for this feature
         callback->CPCLog(e.what());
      }
   }
   else
   {
      callbackRegistered_ = false;
   }
}

TExposureProperty::~TExposureProperty()
{
   if (callbackRegistered_)
   {
      try
      {
         float_feature_->Detach(this);
      }
      catch (exception & e)
      {
         // SDK3 Callback not implemented for this feature
         callback_->CPCLog(e.what());
      }
   }
   //Clean up memory, created as passed in
   callback_->GetCameraDevice()->Release(float_feature_);
}

void TExposureProperty::Update(ISubject * Subject)
{
   //if NOT Poised,... (Snapshot sets this first, then changes trigger silently
   // so once updates get applied, and repoise, snapshot sets true, so no erroneous updates get applied
   if ( !callback_->IsSSCPoised() )
   {
      IFloat * featureSubject = dynamic_cast<IFloat *>(Subject);
      TAndorFloatCache * cache = dynamic_cast<TAndorFloatCache *>(float_feature_);
      if (cache && featureSubject)
      {
         cache->SetCache(featureSubject->Get());
      }
   }
}

void TExposureProperty::setFeatureWithinLimits(double new_value)
{
   try
   {
      if (new_value < float_feature_->Min())
      {
         new_value = float_feature_->Min();
      }
      else if (new_value > float_feature_->Max())
      {
         new_value = float_feature_->Max();
      }
      float_feature_->Set(new_value);
   }
   catch (exception & e)
   {
      callback_->CPCLog(e.what());
   }
}

bool TExposureProperty::valueIsWithinLimits(double new_value)
{
   try
   {
      return new_value > float_feature_->Min() && new_value < float_feature_->Max();
   }
   catch (exception & e)
   {
      callback_->CPCLog(e.what());
      return false;
   }
}

inline bool almostEqual(double val1, double val2, int precisionFactor)
{
   const double base = 10.0;
   double precisionError = 1.0 / pow(base, precisionFactor);
   // Check if val1 and val2 are within precision decimal places
   return ( val1 > (val2 - precisionError) && val1 < (val2 + precisionError)) ? true : false;
}

int TExposureProperty::OnFloat(MM::PropertyBase * pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      TAndorFloatCache * cache = dynamic_cast<TAndorFloatCache *>(float_feature_);
      if (cache)
      {
         pProp->Set(cache->Get(callback_->IsSSCPoised()));
      }
      else
      {
         pProp->Set(float_feature_->Get());
      }

   }
   else if (eAct == MM::AfterSet)
   {
      double new_value = 0.0, current_value = float_feature_->Get();
      pProp->Get(new_value);
      TAndorFloatCache * cache = dynamic_cast<TAndorFloatCache *>(float_feature_);
      if (cache)
      {
         current_value = cache->Get(callback_->IsSSCPoised());
      }
      if (!almostEqual(new_value, current_value, DEC_PLACES_ERROR))
      {
         if(valueIsWithinLimits(new_value) && float_feature_->IsWritable())
         {
           setFeatureWithinLimits(new_value);
         }
         else
         {
            bool wasPoised = callback_->IsSSCPoised();

            if(wasPoised)
              callback_->SSCLeavePoised();

            setFeatureWithinLimits(new_value);

            if(wasPoised)
              callback_->SSCEnterPoised();
         }
      }
   }

   return DEVICE_OK;
}

<|endoftext|>