<commit_before>//===-- Support/FoldingSet.cpp - Uniquing Hash Set --------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements a hash set that can be used to remove duplication of
// nodes in a graph.  This code was originally created by Chris Lattner for use
// with SelectionDAGCSEMap, but was isolated to provide use across the llvm code
// set.
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/FoldingSet.h"
#include "llvm/Support/MathExtras.h"
#include <cassert>
using namespace llvm;

//===----------------------------------------------------------------------===//
// FoldingSetNodeID Implementation

/// Add* - Add various data types to Bit data.
///
void FoldingSetNodeID::AddPointer(const void *Ptr) {
  // Note: this adds pointers to the hash using sizes and endianness that
  // depend on the host.  It doesn't matter however, because hashing on
  // pointer values in inherently unstable.  Nothing  should depend on the
  // ordering of nodes in the folding set.
  intptr_t PtrI = (intptr_t)Ptr;
  Bits.push_back(unsigned(PtrI));
  if (sizeof(intptr_t) > sizeof(unsigned))
    Bits.push_back(unsigned(uint64_t(PtrI) >> 32));
}
void FoldingSetNodeID::AddInteger(signed I) {
  Bits.push_back(I);
}
void FoldingSetNodeID::AddInteger(unsigned I) {
  Bits.push_back(I);
}
void FoldingSetNodeID::AddInteger(int64_t I) {
  AddInteger((uint64_t)I);
}
void FoldingSetNodeID::AddInteger(uint64_t I) {
  Bits.push_back(unsigned(I));

  // If the integer is small, encode it just as 32-bits.
  if ((uint64_t)(int)I != I)
    Bits.push_back(unsigned(I >> 32));
}
void FoldingSetNodeID::AddFloat(float F) {
  Bits.push_back(FloatToBits(F));
}
void FoldingSetNodeID::AddDouble(double D) {
 AddInteger(DoubleToBits(D));
}
void FoldingSetNodeID::AddString(const std::string &String) {
  unsigned Size = String.size();
  Bits.push_back(Size);
  if (!Size) return;

  unsigned Units = Size / 4;
  unsigned Pos = 0;
  const unsigned *Base = (const unsigned *)String.data();

  // If the string is aligned do a bulk transfer.
  if (!((intptr_t)Base & 3)) {
    Bits.append(Base, Base + Units);
    Pos = (Units + 1) * 4;
  } else {
    // Otherwise do it the hard way.
    for ( Pos += 4; Pos <= Size; Pos += 4) {
      unsigned V = ((unsigned char)String[Pos - 4] << 24) |
                   ((unsigned char)String[Pos - 3] << 16) |
                   ((unsigned char)String[Pos - 2] << 8) |
                    (unsigned char)String[Pos - 1];
      Bits.push_back(V);
    }
  }

  // With the leftover bits.
  unsigned V = 0;
  // Pos will have overshot size by 4 - #bytes left over.
  switch (Pos - Size) {
  case 1: V = (V << 8) | (unsigned char)String[Size - 3]; // Fall thru.
  case 2: V = (V << 8) | (unsigned char)String[Size - 2]; // Fall thru.
  case 3: V = (V << 8) | (unsigned char)String[Size - 1]; break;
  default: return; // Nothing left.
  }

  Bits.push_back(V);
}

/// ComputeHash - Compute a strong hash value for this FoldingSetNodeID, used to
/// lookup the node in the FoldingSetImpl.
unsigned FoldingSetNodeID::ComputeHash() const {
  // This is adapted from SuperFastHash by Paul Hsieh.
  unsigned Hash = Bits.size();
  for (const unsigned *BP = &Bits[0], *E = BP+Bits.size(); BP != E; ++BP) {
    unsigned Data = *BP;
    Hash         += Data & 0xFFFF;
    unsigned Tmp  = ((Data >> 16) << 11) ^ Hash;
    Hash          = (Hash << 16) ^ Tmp;
    Hash         += Hash >> 11;
  }

  // Force "avalanching" of final 127 bits.
  Hash ^= Hash << 3;
  Hash += Hash >> 5;
  Hash ^= Hash << 4;
  Hash += Hash >> 17;
  Hash ^= Hash << 25;
  Hash += Hash >> 6;
  return Hash;
}

/// operator== - Used to compare two nodes to each other.
///
bool FoldingSetNodeID::operator==(const FoldingSetNodeID &RHS)const{
  if (Bits.size() != RHS.Bits.size()) return false;
  return memcmp(&Bits[0], &RHS.Bits[0], Bits.size()*sizeof(Bits[0])) == 0;
}

//===----------------------------------------------------------------------===//
/// Helper functions for FoldingSetImpl.

/// GetNextPtr - In order to save space, each bucket is a
/// singly-linked-list. In order to make deletion more efficient, we make
/// the list circular, so we can delete a node without computing its hash.
/// The problem with this is that the start of the hash buckets are not
/// Nodes.  If NextInBucketPtr is a bucket pointer, this method returns null:
/// use GetBucketPtr when this happens.
static FoldingSetImpl::Node *GetNextPtr(void *NextInBucketPtr) {
  // The low bit is set if this is the pointer back to the bucket.
  if (reinterpret_cast<intptr_t>(NextInBucketPtr) & 1)
    return 0;

  return static_cast<FoldingSetImpl::Node*>(NextInBucketPtr);
}

/// testing.
static void **GetBucketPtr(void *NextInBucketPtr) {
  intptr_t Ptr = reinterpret_cast<intptr_t>(NextInBucketPtr);
  assert((Ptr & 1) && "Not a bucket pointer");
  return reinterpret_cast<void**>(Ptr & ~intptr_t(1));
}

/// GetBucketFor - Hash the specified node ID and return the hash bucket for
/// the specified ID.
static void **GetBucketFor(const FoldingSetNodeID &ID,
                           void **Buckets, unsigned NumBuckets) {
  // NumBuckets is always a power of 2.
  unsigned BucketNum = ID.ComputeHash() & (NumBuckets-1);
  return Buckets + BucketNum;
}

//===----------------------------------------------------------------------===//
// FoldingSetImpl Implementation

FoldingSetImpl::FoldingSetImpl(unsigned Log2InitSize) : NumNodes(0) {
  assert(5 < Log2InitSize && Log2InitSize < 32 &&
         "Initial hash table size out of range");
  NumBuckets = 1 << Log2InitSize;
  Buckets = new void*[NumBuckets+1];
  memset(Buckets, 0, NumBuckets*sizeof(void*));

  // Set the very last bucket to be a non-null "pointer".
  Buckets[NumBuckets] = reinterpret_cast<void*>(-2);
}
FoldingSetImpl::~FoldingSetImpl() {
  delete [] Buckets;
}

/// GrowHashTable - Double the size of the hash table and rehash everything.
///
void FoldingSetImpl::GrowHashTable() {
  void **OldBuckets = Buckets;
  unsigned OldNumBuckets = NumBuckets;
  NumBuckets <<= 1;

  // Reset the node count to zero: we're going to reinsert everything.
  NumNodes = 0;

  // Clear out new buckets.
  Buckets = new void*[NumBuckets+1];
  memset(Buckets, 0, NumBuckets*sizeof(void*));

  // Set the very last bucket to be a non-null "pointer".
  Buckets[NumBuckets] = reinterpret_cast<void*>(-1);

  // Walk the old buckets, rehashing nodes into their new place.
  for (unsigned i = 0; i != OldNumBuckets; ++i) {
    void *Probe = OldBuckets[i];
    if (!Probe) continue;
    while (Node *NodeInBucket = GetNextPtr(Probe)) {
      // Figure out the next link, remove NodeInBucket from the old link.
      Probe = NodeInBucket->getNextInBucket();
      NodeInBucket->SetNextInBucket(0);

      // Insert the node into the new bucket, after recomputing the hash.
      FoldingSetNodeID ID;
      GetNodeProfile(ID, NodeInBucket);
      InsertNode(NodeInBucket, GetBucketFor(ID, Buckets, NumBuckets));
    }
  }

  delete[] OldBuckets;
}

/// FindNodeOrInsertPos - Look up the node specified by ID.  If it exists,
/// return it.  If not, return the insertion token that will make insertion
/// faster.
FoldingSetImpl::Node
*FoldingSetImpl::FindNodeOrInsertPos(const FoldingSetNodeID &ID,
                                     void *&InsertPos) {

  void **Bucket = GetBucketFor(ID, Buckets, NumBuckets);
  void *Probe = *Bucket;

  InsertPos = 0;

  while (Node *NodeInBucket = GetNextPtr(Probe)) {
    FoldingSetNodeID OtherID;
    GetNodeProfile(OtherID, NodeInBucket);
    if (OtherID == ID)
      return NodeInBucket;

    Probe = NodeInBucket->getNextInBucket();
  }

  // Didn't find the node, return null with the bucket as the InsertPos.
  InsertPos = Bucket;
  return 0;
}

/// InsertNode - Insert the specified node into the folding set, knowing that it
/// is not already in the map.  InsertPos must be obtained from
/// FindNodeOrInsertPos.
void FoldingSetImpl::InsertNode(Node *N, void *InsertPos) {
  assert(N->getNextInBucket() == 0);
  // Do we need to grow the hashtable?
  if (NumNodes+1 > NumBuckets*2) {
    GrowHashTable();
    FoldingSetNodeID ID;
    GetNodeProfile(ID, N);
    InsertPos = GetBucketFor(ID, Buckets, NumBuckets);
  }

  ++NumNodes;

  /// The insert position is actually a bucket pointer.
  void **Bucket = static_cast<void**>(InsertPos);

  void *Next = *Bucket;

  // If this is the first insertion into this bucket, its next pointer will be
  // null.  Pretend as if it pointed to itself, setting the low bit to indicate
  // that it is a pointer to the bucket.
  if (Next == 0)
    Next = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(Bucket)|1);

  // Set the node's next pointer, and make the bucket point to the node.
  N->SetNextInBucket(Next);
  *Bucket = N;
}

/// RemoveNode - Remove a node from the folding set, returning true if one was
/// removed or false if the node was not in the folding set.
bool FoldingSetImpl::RemoveNode(Node *N) {
  // Because each bucket is a circular list, we don't need to compute N's hash
  // to remove it.
  void *Ptr = N->getNextInBucket();
  if (Ptr == 0) return false;  // Not in folding set.

  --NumNodes;
  N->SetNextInBucket(0);

  // Remember what N originally pointed to, either a bucket or another node.
  void *NodeNextPtr = Ptr;

  // Chase around the list until we find the node (or bucket) which points to N.
  while (true) {
    if (Node *NodeInBucket = GetNextPtr(Ptr)) {
      // Advance pointer.
      Ptr = NodeInBucket->getNextInBucket();

      // We found a node that points to N, change it to point to N's next node,
      // removing N from the list.
      if (Ptr == N) {
        NodeInBucket->SetNextInBucket(NodeNextPtr);
        return true;
      }
    } else {
      void **Bucket = GetBucketPtr(Ptr);
      Ptr = *Bucket;

      // If we found that the bucket points to N, update the bucket to point to
      // whatever is next.
      if (Ptr == N) {
        *Bucket = NodeNextPtr;
        return true;
      }
    }
  }
}

/// GetOrInsertNode - If there is an existing simple Node exactly
/// equal to the specified node, return it.  Otherwise, insert 'N' and it
/// instead.
FoldingSetImpl::Node *FoldingSetImpl::GetOrInsertNode(FoldingSetImpl::Node *N) {
  FoldingSetNodeID ID;
  GetNodeProfile(ID, N);
  void *IP;
  if (Node *E = FindNodeOrInsertPos(ID, IP))
    return E;
  InsertNode(N, IP);
  return N;
}

//===----------------------------------------------------------------------===//
// FoldingSetIteratorImpl Implementation

FoldingSetIteratorImpl::FoldingSetIteratorImpl(void **Bucket) {
  // Skip to the first non-null non-self-cycle bucket.
  while (*Bucket == 0 || GetNextPtr(*Bucket) == 0)
    ++Bucket;

  NodePtr = static_cast<FoldingSetNode*>(*Bucket);
}

void FoldingSetIteratorImpl::advance() {
  // If there is another link within this bucket, go to it.
  void *Probe = NodePtr->getNextInBucket();

  if (FoldingSetNode *NextNodeInBucket = GetNextPtr(Probe))
    NodePtr = NextNodeInBucket;
  else {
    // Otherwise, this is the last link in this bucket.
    void **Bucket = GetBucketPtr(Probe);

    // Skip to the next non-null non-self-cycle bucket.
    do {
      ++Bucket;
    } while (*Bucket == 0 || GetNextPtr(*Bucket) == 0);

    NodePtr = static_cast<FoldingSetNode*>(*Bucket);
  }
}

//===----------------------------------------------------------------------===//
// FoldingSetBucketIteratorImpl Implementation

FoldingSetBucketIteratorImpl::FoldingSetBucketIteratorImpl(void **Bucket) {
  Ptr = (*Bucket == 0 || GetNextPtr(*Bucket) == 0) ? (void*) Bucket : *Bucket;
}
<commit_msg>Fixed bug in FoldingSetIteratorImpl where we did not correctly check if we had reached the "fake bucket" after the last bucket, allowing the iterator in some cases to run off the end of the hashtable.<commit_after>//===-- Support/FoldingSet.cpp - Uniquing Hash Set --------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements a hash set that can be used to remove duplication of
// nodes in a graph.  This code was originally created by Chris Lattner for use
// with SelectionDAGCSEMap, but was isolated to provide use across the llvm code
// set.
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/FoldingSet.h"
#include "llvm/Support/MathExtras.h"
#include <cassert>
using namespace llvm;

//===----------------------------------------------------------------------===//
// FoldingSetNodeID Implementation

/// Add* - Add various data types to Bit data.
///
void FoldingSetNodeID::AddPointer(const void *Ptr) {
  // Note: this adds pointers to the hash using sizes and endianness that
  // depend on the host.  It doesn't matter however, because hashing on
  // pointer values in inherently unstable.  Nothing  should depend on the
  // ordering of nodes in the folding set.
  intptr_t PtrI = (intptr_t)Ptr;
  Bits.push_back(unsigned(PtrI));
  if (sizeof(intptr_t) > sizeof(unsigned))
    Bits.push_back(unsigned(uint64_t(PtrI) >> 32));
}
void FoldingSetNodeID::AddInteger(signed I) {
  Bits.push_back(I);
}
void FoldingSetNodeID::AddInteger(unsigned I) {
  Bits.push_back(I);
}
void FoldingSetNodeID::AddInteger(int64_t I) {
  AddInteger((uint64_t)I);
}
void FoldingSetNodeID::AddInteger(uint64_t I) {
  Bits.push_back(unsigned(I));

  // If the integer is small, encode it just as 32-bits.
  if ((uint64_t)(int)I != I)
    Bits.push_back(unsigned(I >> 32));
}
void FoldingSetNodeID::AddFloat(float F) {
  Bits.push_back(FloatToBits(F));
}
void FoldingSetNodeID::AddDouble(double D) {
 AddInteger(DoubleToBits(D));
}
void FoldingSetNodeID::AddString(const std::string &String) {
  unsigned Size = String.size();
  Bits.push_back(Size);
  if (!Size) return;

  unsigned Units = Size / 4;
  unsigned Pos = 0;
  const unsigned *Base = (const unsigned *)String.data();

  // If the string is aligned do a bulk transfer.
  if (!((intptr_t)Base & 3)) {
    Bits.append(Base, Base + Units);
    Pos = (Units + 1) * 4;
  } else {
    // Otherwise do it the hard way.
    for ( Pos += 4; Pos <= Size; Pos += 4) {
      unsigned V = ((unsigned char)String[Pos - 4] << 24) |
                   ((unsigned char)String[Pos - 3] << 16) |
                   ((unsigned char)String[Pos - 2] << 8) |
                    (unsigned char)String[Pos - 1];
      Bits.push_back(V);
    }
  }

  // With the leftover bits.
  unsigned V = 0;
  // Pos will have overshot size by 4 - #bytes left over.
  switch (Pos - Size) {
  case 1: V = (V << 8) | (unsigned char)String[Size - 3]; // Fall thru.
  case 2: V = (V << 8) | (unsigned char)String[Size - 2]; // Fall thru.
  case 3: V = (V << 8) | (unsigned char)String[Size - 1]; break;
  default: return; // Nothing left.
  }

  Bits.push_back(V);
}

/// ComputeHash - Compute a strong hash value for this FoldingSetNodeID, used to
/// lookup the node in the FoldingSetImpl.
unsigned FoldingSetNodeID::ComputeHash() const {
  // This is adapted from SuperFastHash by Paul Hsieh.
  unsigned Hash = Bits.size();
  for (const unsigned *BP = &Bits[0], *E = BP+Bits.size(); BP != E; ++BP) {
    unsigned Data = *BP;
    Hash         += Data & 0xFFFF;
    unsigned Tmp  = ((Data >> 16) << 11) ^ Hash;
    Hash          = (Hash << 16) ^ Tmp;
    Hash         += Hash >> 11;
  }

  // Force "avalanching" of final 127 bits.
  Hash ^= Hash << 3;
  Hash += Hash >> 5;
  Hash ^= Hash << 4;
  Hash += Hash >> 17;
  Hash ^= Hash << 25;
  Hash += Hash >> 6;
  return Hash;
}

/// operator== - Used to compare two nodes to each other.
///
bool FoldingSetNodeID::operator==(const FoldingSetNodeID &RHS)const{
  if (Bits.size() != RHS.Bits.size()) return false;
  return memcmp(&Bits[0], &RHS.Bits[0], Bits.size()*sizeof(Bits[0])) == 0;
}

//===----------------------------------------------------------------------===//
/// Helper functions for FoldingSetImpl.

/// GetNextPtr - In order to save space, each bucket is a
/// singly-linked-list. In order to make deletion more efficient, we make
/// the list circular, so we can delete a node without computing its hash.
/// The problem with this is that the start of the hash buckets are not
/// Nodes.  If NextInBucketPtr is a bucket pointer, this method returns null:
/// use GetBucketPtr when this happens.
static FoldingSetImpl::Node *GetNextPtr(void *NextInBucketPtr) {
  // The low bit is set if this is the pointer back to the bucket.
  if (reinterpret_cast<intptr_t>(NextInBucketPtr) & 1)
    return 0;

  return static_cast<FoldingSetImpl::Node*>(NextInBucketPtr);
}

/// testing.
static void **GetBucketPtr(void *NextInBucketPtr) {
  intptr_t Ptr = reinterpret_cast<intptr_t>(NextInBucketPtr);
  assert((Ptr & 1) && "Not a bucket pointer");
  return reinterpret_cast<void**>(Ptr & ~intptr_t(1));
}

/// GetBucketFor - Hash the specified node ID and return the hash bucket for
/// the specified ID.
static void **GetBucketFor(const FoldingSetNodeID &ID,
                           void **Buckets, unsigned NumBuckets) {
  // NumBuckets is always a power of 2.
  unsigned BucketNum = ID.ComputeHash() & (NumBuckets-1);
  return Buckets + BucketNum;
}

//===----------------------------------------------------------------------===//
// FoldingSetImpl Implementation

FoldingSetImpl::FoldingSetImpl(unsigned Log2InitSize) : NumNodes(0) {
  assert(5 < Log2InitSize && Log2InitSize < 32 &&
         "Initial hash table size out of range");
  NumBuckets = 1 << Log2InitSize;
  Buckets = new void*[NumBuckets+1];
  memset(Buckets, 0, NumBuckets*sizeof(void*));

  // Set the very last bucket to be a non-null "pointer".
  Buckets[NumBuckets] = reinterpret_cast<void*>(-2);
}
FoldingSetImpl::~FoldingSetImpl() {
  delete [] Buckets;
}

/// GrowHashTable - Double the size of the hash table and rehash everything.
///
void FoldingSetImpl::GrowHashTable() {
  void **OldBuckets = Buckets;
  unsigned OldNumBuckets = NumBuckets;
  NumBuckets <<= 1;

  // Reset the node count to zero: we're going to reinsert everything.
  NumNodes = 0;

  // Clear out new buckets.
  Buckets = new void*[NumBuckets+1];
  memset(Buckets, 0, NumBuckets*sizeof(void*));

  // Set the very last bucket to be a non-null "pointer".
  Buckets[NumBuckets] = reinterpret_cast<void*>(-1);

  // Walk the old buckets, rehashing nodes into their new place.
  for (unsigned i = 0; i != OldNumBuckets; ++i) {
    void *Probe = OldBuckets[i];
    if (!Probe) continue;
    while (Node *NodeInBucket = GetNextPtr(Probe)) {
      // Figure out the next link, remove NodeInBucket from the old link.
      Probe = NodeInBucket->getNextInBucket();
      NodeInBucket->SetNextInBucket(0);

      // Insert the node into the new bucket, after recomputing the hash.
      FoldingSetNodeID ID;
      GetNodeProfile(ID, NodeInBucket);
      InsertNode(NodeInBucket, GetBucketFor(ID, Buckets, NumBuckets));
    }
  }

  delete[] OldBuckets;
}

/// FindNodeOrInsertPos - Look up the node specified by ID.  If it exists,
/// return it.  If not, return the insertion token that will make insertion
/// faster.
FoldingSetImpl::Node
*FoldingSetImpl::FindNodeOrInsertPos(const FoldingSetNodeID &ID,
                                     void *&InsertPos) {

  void **Bucket = GetBucketFor(ID, Buckets, NumBuckets);
  void *Probe = *Bucket;

  InsertPos = 0;

  while (Node *NodeInBucket = GetNextPtr(Probe)) {
    FoldingSetNodeID OtherID;
    GetNodeProfile(OtherID, NodeInBucket);
    if (OtherID == ID)
      return NodeInBucket;

    Probe = NodeInBucket->getNextInBucket();
  }

  // Didn't find the node, return null with the bucket as the InsertPos.
  InsertPos = Bucket;
  return 0;
}

/// InsertNode - Insert the specified node into the folding set, knowing that it
/// is not already in the map.  InsertPos must be obtained from
/// FindNodeOrInsertPos.
void FoldingSetImpl::InsertNode(Node *N, void *InsertPos) {
  assert(N->getNextInBucket() == 0);
  // Do we need to grow the hashtable?
  if (NumNodes+1 > NumBuckets*2) {
    GrowHashTable();
    FoldingSetNodeID ID;
    GetNodeProfile(ID, N);
    InsertPos = GetBucketFor(ID, Buckets, NumBuckets);
  }

  ++NumNodes;

  /// The insert position is actually a bucket pointer.
  void **Bucket = static_cast<void**>(InsertPos);

  void *Next = *Bucket;

  // If this is the first insertion into this bucket, its next pointer will be
  // null.  Pretend as if it pointed to itself, setting the low bit to indicate
  // that it is a pointer to the bucket.
  if (Next == 0)
    Next = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(Bucket)|1);

  // Set the node's next pointer, and make the bucket point to the node.
  N->SetNextInBucket(Next);
  *Bucket = N;
}

/// RemoveNode - Remove a node from the folding set, returning true if one was
/// removed or false if the node was not in the folding set.
bool FoldingSetImpl::RemoveNode(Node *N) {
  // Because each bucket is a circular list, we don't need to compute N's hash
  // to remove it.
  void *Ptr = N->getNextInBucket();
  if (Ptr == 0) return false;  // Not in folding set.

  --NumNodes;
  N->SetNextInBucket(0);

  // Remember what N originally pointed to, either a bucket or another node.
  void *NodeNextPtr = Ptr;

  // Chase around the list until we find the node (or bucket) which points to N.
  while (true) {
    if (Node *NodeInBucket = GetNextPtr(Ptr)) {
      // Advance pointer.
      Ptr = NodeInBucket->getNextInBucket();

      // We found a node that points to N, change it to point to N's next node,
      // removing N from the list.
      if (Ptr == N) {
        NodeInBucket->SetNextInBucket(NodeNextPtr);
        return true;
      }
    } else {
      void **Bucket = GetBucketPtr(Ptr);
      Ptr = *Bucket;

      // If we found that the bucket points to N, update the bucket to point to
      // whatever is next.
      if (Ptr == N) {
        *Bucket = NodeNextPtr;
        return true;
      }
    }
  }
}

/// GetOrInsertNode - If there is an existing simple Node exactly
/// equal to the specified node, return it.  Otherwise, insert 'N' and it
/// instead.
FoldingSetImpl::Node *FoldingSetImpl::GetOrInsertNode(FoldingSetImpl::Node *N) {
  FoldingSetNodeID ID;
  GetNodeProfile(ID, N);
  void *IP;
  if (Node *E = FindNodeOrInsertPos(ID, IP))
    return E;
  InsertNode(N, IP);
  return N;
}

//===----------------------------------------------------------------------===//
// FoldingSetIteratorImpl Implementation

FoldingSetIteratorImpl::FoldingSetIteratorImpl(void **Bucket) {
  // Skip to the first non-null non-self-cycle bucket.
  while (*Bucket != reinterpret_cast<void*>(-1) &&
         (*Bucket == 0 || GetNextPtr(*Bucket) == 0))
    ++Bucket;

  NodePtr = static_cast<FoldingSetNode*>(*Bucket);
}

void FoldingSetIteratorImpl::advance() {
  // If there is another link within this bucket, go to it.
  void *Probe = NodePtr->getNextInBucket();

  if (FoldingSetNode *NextNodeInBucket = GetNextPtr(Probe))
    NodePtr = NextNodeInBucket;
  else {
    // Otherwise, this is the last link in this bucket.
    void **Bucket = GetBucketPtr(Probe);

    // Skip to the next non-null non-self-cycle bucket.
    do {
      ++Bucket;
    } while (*Bucket != reinterpret_cast<void*>(-1) &&
             (*Bucket == 0 || GetNextPtr(*Bucket) == 0));

    NodePtr = static_cast<FoldingSetNode*>(*Bucket);
  }
}

//===----------------------------------------------------------------------===//
// FoldingSetBucketIteratorImpl Implementation

FoldingSetBucketIteratorImpl::FoldingSetBucketIteratorImpl(void **Bucket) {
  Ptr = (*Bucket == 0 || GetNextPtr(*Bucket) == 0) ? (void*) Bucket : *Bucket;
}
<|endoftext|>


<commit_before>//===-- Instruction.cpp - Implement the Instruction class -----------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the Instruction class for the VMCore library.
//
//===----------------------------------------------------------------------===//

#include "llvm/Type.h"
#include "llvm/Instructions.h"
#include "llvm/IntrinsicInst.h"
#include "llvm/Function.h"
#include "llvm/Support/LeakDetector.h"
using namespace llvm;

Instruction::Instruction(const Type *ty, unsigned it, Use *Ops, unsigned NumOps,
                         const std::string &Name, Instruction *InsertBefore)
  : User(ty, Value::InstructionVal + it, Ops, NumOps), Parent(0) {
  // Make sure that we get added to a basicblock
  LeakDetector::addGarbageObject(this);

  // If requested, insert this instruction into a basic block...
  if (InsertBefore) {
    assert(InsertBefore->getParent() &&
           "Instruction to insert before is not in a basic block!");
    InsertBefore->getParent()->getInstList().insert(InsertBefore, this);
  }
  setName(Name);
}

Instruction::Instruction(const Type *ty, unsigned it, Use *Ops, unsigned NumOps,
                         const std::string &Name, BasicBlock *InsertAtEnd)
  : User(ty, Value::InstructionVal + it, Ops, NumOps), Parent(0) {
  // Make sure that we get added to a basicblock
  LeakDetector::addGarbageObject(this);

  // append this instruction into the basic block
  assert(InsertAtEnd && "Basic block to append to may not be NULL!");
  InsertAtEnd->getInstList().push_back(this);
  setName(Name);
}

Instruction::Instruction(const Type *ty, unsigned it, Use *Ops, unsigned NumOps,
                         const char *Name, Instruction *InsertBefore)
  : User(ty, Value::InstructionVal + it, Ops, NumOps), Parent(0) {
  // Make sure that we get added to a basicblock
  LeakDetector::addGarbageObject(this);

  // If requested, insert this instruction into a basic block...
  if (InsertBefore) {
    assert(InsertBefore->getParent() &&
           "Instruction to insert before is not in a basic block!");
    InsertBefore->getParent()->getInstList().insert(InsertBefore, this);
  }
  if (Name && *Name) setName(Name);
}

Instruction::Instruction(const Type *ty, unsigned it, Use *Ops, unsigned NumOps,
                         const char *Name, BasicBlock *InsertAtEnd)
  : User(ty, Value::InstructionVal + it, Ops, NumOps), Parent(0) {
  // Make sure that we get added to a basicblock
  LeakDetector::addGarbageObject(this);

  // append this instruction into the basic block
  assert(InsertAtEnd && "Basic block to append to may not be NULL!");
  InsertAtEnd->getInstList().push_back(this);
  if (Name && *Name) setName(Name);
}

// Out of line virtual method, so the vtable, etc has a home.
Instruction::~Instruction() {
  assert(Parent == 0 && "Instruction still linked in the program!");
}

void Instruction::setParent(BasicBlock *P) {
  if (getParent()) {
    if (!P) LeakDetector::addGarbageObject(this);
  } else {
    if (P) LeakDetector::removeGarbageObject(this);
  }

  Parent = P;
}

void Instruction::removeFromParent() {
  getParent()->getInstList().remove(this);
}

void Instruction::eraseFromParent() {
  getParent()->getInstList().erase(this);
}

/// moveBefore - Unlink this instruction from its current basic block and
/// insert it into the basic block that MovePos lives in, right before
/// MovePos.
void Instruction::moveBefore(Instruction *MovePos) {
  MovePos->getParent()->getInstList().splice(MovePos,getParent()->getInstList(),
                                             this);
}

const char *Instruction::getOpcodeName(unsigned OpCode) {
  switch (OpCode) {
  // Terminators
  case Ret:    return "ret";
  case Br:     return "br";
  case Switch: return "switch";
  case Invoke: return "invoke";
  case Unwind: return "unwind";
  case Unreachable: return "unreachable";

  // Standard binary operators...
  case Add: return "add";
  case Sub: return "sub";
  case Mul: return "mul";
  case UDiv: return "udiv";
  case SDiv: return "sdiv";
  case FDiv: return "fdiv";
  case URem: return "urem";
  case SRem: return "srem";
  case FRem: return "frem";

  // Logical operators...
  case And: return "and";
  case Or : return "or";
  case Xor: return "xor";

  // Memory instructions...
  case Malloc:        return "malloc";
  case Free:          return "free";
  case Alloca:        return "alloca";
  case Load:          return "load";
  case Store:         return "store";
  case GetElementPtr: return "getelementptr";

  // Convert instructions...
  case Trunc:     return "trunc";
  case ZExt:      return "zext";
  case SExt:      return "sext";
  case FPTrunc:   return "fptrunc";
  case FPExt:     return "fpext";
  case FPToUI:    return "fptoui";
  case FPToSI:    return "fptosi";
  case UIToFP:    return "uitofp";
  case SIToFP:    return "sitofp";
  case IntToPtr:  return "inttoptr";
  case PtrToInt:  return "ptrtoint";
  case BitCast:   return "bitcast";

  // Other instructions...
  case ICmp:           return "icmp";
  case FCmp:           return "fcmp";
  case PHI:            return "phi";
  case Select:         return "select";
  case Call:           return "call";
  case Shl:            return "shl";
  case LShr:           return "lshr";
  case AShr:           return "ashr";
  case VAArg:          return "va_arg";
  case ExtractElement: return "extractelement";
  case InsertElement:  return "insertelement";
  case ShuffleVector:  return "shufflevector";

  default: return "<Invalid operator> ";
  }

  return 0;
}

/// isIdenticalTo - Return true if the specified instruction is exactly
/// identical to the current one.  This means that all operands match and any
/// extra information (e.g. load is volatile) agree.
bool Instruction::isIdenticalTo(Instruction *I) const {
  if (getOpcode() != I->getOpcode() ||
      getNumOperands() != I->getNumOperands() ||
      getType() != I->getType())
    return false;

  // We have two instructions of identical opcode and #operands.  Check to see
  // if all operands are the same.
  for (unsigned i = 0, e = getNumOperands(); i != e; ++i)
    if (getOperand(i) != I->getOperand(i))
      return false;

  // Check special state that is a part of some instructions.
  if (const LoadInst *LI = dyn_cast<LoadInst>(this))
    return LI->isVolatile() == cast<LoadInst>(I)->isVolatile();
  if (const StoreInst *SI = dyn_cast<StoreInst>(this))
    return SI->isVolatile() == cast<StoreInst>(I)->isVolatile();
  if (const CmpInst *CI = dyn_cast<CmpInst>(this))
    return CI->getPredicate() == cast<CmpInst>(I)->getPredicate();
  if (const CallInst *CI = dyn_cast<CallInst>(this))
    return CI->isTailCall() == cast<CallInst>(I)->isTailCall();
  return true;
}

// isSameOperationAs
bool Instruction::isSameOperationAs(Instruction *I) const {
  if (getOpcode() != I->getOpcode() || getType() != I->getType() ||
      getNumOperands() != I->getNumOperands())
    return false;

  // We have two instructions of identical opcode and #operands.  Check to see
  // if all operands are the same type
  for (unsigned i = 0, e = getNumOperands(); i != e; ++i)
    if (getOperand(i)->getType() != I->getOperand(i)->getType())
      return false;

  // Check special state that is a part of some instructions.
  if (const LoadInst *LI = dyn_cast<LoadInst>(this))
    return LI->isVolatile() == cast<LoadInst>(I)->isVolatile();
  if (const StoreInst *SI = dyn_cast<StoreInst>(this))
    return SI->isVolatile() == cast<StoreInst>(I)->isVolatile();
  if (const CmpInst *CI = dyn_cast<CmpInst>(this))
    return CI->getPredicate() == cast<CmpInst>(I)->getPredicate();
  if (const CallInst *CI = dyn_cast<CallInst>(this))
    return CI->isTailCall() == cast<CallInst>(I)->isTailCall();

  return true;
}

/// mayWriteToMemory - Return true if this instruction may modify memory.
///
bool Instruction::mayWriteToMemory() const {
  switch (getOpcode()) {
  default: return false;
  case Instruction::Free:
  case Instruction::Store:
  case Instruction::Invoke:
  case Instruction::VAArg:
    return true;
  case Instruction::Call:
    if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(this)) {
      // If the intrinsic doesn't write memory, it is safe.
    }
    return true;
  case Instruction::Load:
    return cast<LoadInst>(this)->isVolatile();
  }
}

/// isAssociative - Return true if the instruction is associative:
///
///   Associative operators satisfy:  x op (y op z) === (x op y) op z)
///
/// In LLVM, the Add, Mul, And, Or, and Xor operators are associative, when not
/// applied to floating point types.
///
bool Instruction::isAssociative(unsigned Opcode, const Type *Ty) {
  if (Opcode == And || Opcode == Or || Opcode == Xor)
    return true;

  // Add/Mul reassociate unless they are FP or FP vectors.
  if (Opcode == Add || Opcode == Mul)
    return !Ty->isFPOrFPVector();
  return 0;
}

/// isCommutative - Return true if the instruction is commutative:
///
///   Commutative operators satisfy: (x op y) === (y op x)
///
/// In LLVM, these are the associative operators, plus SetEQ and SetNE, when
/// applied to any type.
///
bool Instruction::isCommutative(unsigned op) {
  switch (op) {
  case Add:
  case Mul:
  case And:
  case Or:
  case Xor:
    return true;
  default:
    return false;
  }
}

/// isTrappingInstruction - Return true if the instruction may trap.
///
bool Instruction::isTrapping(unsigned op) {
  switch(op) {
  case UDiv:
  case SDiv:
  case FDiv:
  case URem:
  case SRem:
  case FRem:
  case Load:
  case Store:
  case Call:
  case Invoke:
    return true;
  default:
    return false;
  }
}
<commit_msg>Add a FIXME for unwritten code.<commit_after>//===-- Instruction.cpp - Implement the Instruction class -----------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the Instruction class for the VMCore library.
//
//===----------------------------------------------------------------------===//

#include "llvm/Type.h"
#include "llvm/Instructions.h"
#include "llvm/IntrinsicInst.h"
#include "llvm/Function.h"
#include "llvm/Support/LeakDetector.h"
using namespace llvm;

Instruction::Instruction(const Type *ty, unsigned it, Use *Ops, unsigned NumOps,
                         const std::string &Name, Instruction *InsertBefore)
  : User(ty, Value::InstructionVal + it, Ops, NumOps), Parent(0) {
  // Make sure that we get added to a basicblock
  LeakDetector::addGarbageObject(this);

  // If requested, insert this instruction into a basic block...
  if (InsertBefore) {
    assert(InsertBefore->getParent() &&
           "Instruction to insert before is not in a basic block!");
    InsertBefore->getParent()->getInstList().insert(InsertBefore, this);
  }
  setName(Name);
}

Instruction::Instruction(const Type *ty, unsigned it, Use *Ops, unsigned NumOps,
                         const std::string &Name, BasicBlock *InsertAtEnd)
  : User(ty, Value::InstructionVal + it, Ops, NumOps), Parent(0) {
  // Make sure that we get added to a basicblock
  LeakDetector::addGarbageObject(this);

  // append this instruction into the basic block
  assert(InsertAtEnd && "Basic block to append to may not be NULL!");
  InsertAtEnd->getInstList().push_back(this);
  setName(Name);
}

Instruction::Instruction(const Type *ty, unsigned it, Use *Ops, unsigned NumOps,
                         const char *Name, Instruction *InsertBefore)
  : User(ty, Value::InstructionVal + it, Ops, NumOps), Parent(0) {
  // Make sure that we get added to a basicblock
  LeakDetector::addGarbageObject(this);

  // If requested, insert this instruction into a basic block...
  if (InsertBefore) {
    assert(InsertBefore->getParent() &&
           "Instruction to insert before is not in a basic block!");
    InsertBefore->getParent()->getInstList().insert(InsertBefore, this);
  }
  if (Name && *Name) setName(Name);
}

Instruction::Instruction(const Type *ty, unsigned it, Use *Ops, unsigned NumOps,
                         const char *Name, BasicBlock *InsertAtEnd)
  : User(ty, Value::InstructionVal + it, Ops, NumOps), Parent(0) {
  // Make sure that we get added to a basicblock
  LeakDetector::addGarbageObject(this);

  // append this instruction into the basic block
  assert(InsertAtEnd && "Basic block to append to may not be NULL!");
  InsertAtEnd->getInstList().push_back(this);
  if (Name && *Name) setName(Name);
}

// Out of line virtual method, so the vtable, etc has a home.
Instruction::~Instruction() {
  assert(Parent == 0 && "Instruction still linked in the program!");
}

void Instruction::setParent(BasicBlock *P) {
  if (getParent()) {
    if (!P) LeakDetector::addGarbageObject(this);
  } else {
    if (P) LeakDetector::removeGarbageObject(this);
  }

  Parent = P;
}

void Instruction::removeFromParent() {
  getParent()->getInstList().remove(this);
}

void Instruction::eraseFromParent() {
  getParent()->getInstList().erase(this);
}

/// moveBefore - Unlink this instruction from its current basic block and
/// insert it into the basic block that MovePos lives in, right before
/// MovePos.
void Instruction::moveBefore(Instruction *MovePos) {
  MovePos->getParent()->getInstList().splice(MovePos,getParent()->getInstList(),
                                             this);
}

const char *Instruction::getOpcodeName(unsigned OpCode) {
  switch (OpCode) {
  // Terminators
  case Ret:    return "ret";
  case Br:     return "br";
  case Switch: return "switch";
  case Invoke: return "invoke";
  case Unwind: return "unwind";
  case Unreachable: return "unreachable";

  // Standard binary operators...
  case Add: return "add";
  case Sub: return "sub";
  case Mul: return "mul";
  case UDiv: return "udiv";
  case SDiv: return "sdiv";
  case FDiv: return "fdiv";
  case URem: return "urem";
  case SRem: return "srem";
  case FRem: return "frem";

  // Logical operators...
  case And: return "and";
  case Or : return "or";
  case Xor: return "xor";

  // Memory instructions...
  case Malloc:        return "malloc";
  case Free:          return "free";
  case Alloca:        return "alloca";
  case Load:          return "load";
  case Store:         return "store";
  case GetElementPtr: return "getelementptr";

  // Convert instructions...
  case Trunc:     return "trunc";
  case ZExt:      return "zext";
  case SExt:      return "sext";
  case FPTrunc:   return "fptrunc";
  case FPExt:     return "fpext";
  case FPToUI:    return "fptoui";
  case FPToSI:    return "fptosi";
  case UIToFP:    return "uitofp";
  case SIToFP:    return "sitofp";
  case IntToPtr:  return "inttoptr";
  case PtrToInt:  return "ptrtoint";
  case BitCast:   return "bitcast";

  // Other instructions...
  case ICmp:           return "icmp";
  case FCmp:           return "fcmp";
  case PHI:            return "phi";
  case Select:         return "select";
  case Call:           return "call";
  case Shl:            return "shl";
  case LShr:           return "lshr";
  case AShr:           return "ashr";
  case VAArg:          return "va_arg";
  case ExtractElement: return "extractelement";
  case InsertElement:  return "insertelement";
  case ShuffleVector:  return "shufflevector";

  default: return "<Invalid operator> ";
  }

  return 0;
}

/// isIdenticalTo - Return true if the specified instruction is exactly
/// identical to the current one.  This means that all operands match and any
/// extra information (e.g. load is volatile) agree.
bool Instruction::isIdenticalTo(Instruction *I) const {
  if (getOpcode() != I->getOpcode() ||
      getNumOperands() != I->getNumOperands() ||
      getType() != I->getType())
    return false;

  // We have two instructions of identical opcode and #operands.  Check to see
  // if all operands are the same.
  for (unsigned i = 0, e = getNumOperands(); i != e; ++i)
    if (getOperand(i) != I->getOperand(i))
      return false;

  // Check special state that is a part of some instructions.
  if (const LoadInst *LI = dyn_cast<LoadInst>(this))
    return LI->isVolatile() == cast<LoadInst>(I)->isVolatile();
  if (const StoreInst *SI = dyn_cast<StoreInst>(this))
    return SI->isVolatile() == cast<StoreInst>(I)->isVolatile();
  if (const CmpInst *CI = dyn_cast<CmpInst>(this))
    return CI->getPredicate() == cast<CmpInst>(I)->getPredicate();
  if (const CallInst *CI = dyn_cast<CallInst>(this))
    return CI->isTailCall() == cast<CallInst>(I)->isTailCall();
  return true;
}

// isSameOperationAs
bool Instruction::isSameOperationAs(Instruction *I) const {
  if (getOpcode() != I->getOpcode() || getType() != I->getType() ||
      getNumOperands() != I->getNumOperands())
    return false;

  // We have two instructions of identical opcode and #operands.  Check to see
  // if all operands are the same type
  for (unsigned i = 0, e = getNumOperands(); i != e; ++i)
    if (getOperand(i)->getType() != I->getOperand(i)->getType())
      return false;

  // Check special state that is a part of some instructions.
  if (const LoadInst *LI = dyn_cast<LoadInst>(this))
    return LI->isVolatile() == cast<LoadInst>(I)->isVolatile();
  if (const StoreInst *SI = dyn_cast<StoreInst>(this))
    return SI->isVolatile() == cast<StoreInst>(I)->isVolatile();
  if (const CmpInst *CI = dyn_cast<CmpInst>(this))
    return CI->getPredicate() == cast<CmpInst>(I)->getPredicate();
  if (const CallInst *CI = dyn_cast<CallInst>(this))
    return CI->isTailCall() == cast<CallInst>(I)->isTailCall();

  return true;
}

/// mayWriteToMemory - Return true if this instruction may modify memory.
///
bool Instruction::mayWriteToMemory() const {
  switch (getOpcode()) {
  default: return false;
  case Instruction::Free:
  case Instruction::Store:
  case Instruction::Invoke:
  case Instruction::VAArg:
    return true;
  case Instruction::Call:
    if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(this)) {
      // If the intrinsic doesn't write memory, it is safe.
      // FIXME: this is obviously supposed to determine which  intrinsics
      // don't write to memory, but hasn't been implemented yet.
    }
    return true;
  case Instruction::Load:
    return cast<LoadInst>(this)->isVolatile();
  }
}

/// isAssociative - Return true if the instruction is associative:
///
///   Associative operators satisfy:  x op (y op z) === (x op y) op z)
///
/// In LLVM, the Add, Mul, And, Or, and Xor operators are associative, when not
/// applied to floating point types.
///
bool Instruction::isAssociative(unsigned Opcode, const Type *Ty) {
  if (Opcode == And || Opcode == Or || Opcode == Xor)
    return true;

  // Add/Mul reassociate unless they are FP or FP vectors.
  if (Opcode == Add || Opcode == Mul)
    return !Ty->isFPOrFPVector();
  return 0;
}

/// isCommutative - Return true if the instruction is commutative:
///
///   Commutative operators satisfy: (x op y) === (y op x)
///
/// In LLVM, these are the associative operators, plus SetEQ and SetNE, when
/// applied to any type.
///
bool Instruction::isCommutative(unsigned op) {
  switch (op) {
  case Add:
  case Mul:
  case And:
  case Or:
  case Xor:
    return true;
  default:
    return false;
  }
}

/// isTrappingInstruction - Return true if the instruction may trap.
///
bool Instruction::isTrapping(unsigned op) {
  switch(op) {
  case UDiv:
  case SDiv:
  case FDiv:
  case URem:
  case SRem:
  case FRem:
  case Load:
  case Store:
  case Call:
  case Invoke:
    return true;
  default:
    return false;
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
 *
 * 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.
 *
 */

#include "loader-png.h"

#include <cstring>
#include <cstdlib>

#include <zlib.h>
#include <png.h>

#include <dali/integration-api/bitmap.h>
#include <dali/integration-api/debug.h>
#include <dali/public-api/images/image.h>
#include "dali/public-api/math/math-utils.h"
#include "dali/public-api/math/vector2.h"
#include "platform-capabilities.h"

namespace Dali
{

using Integration::Bitmap;
using Dali::Integration::PixelBuffer;

namespace TizenPlatform
{

namespace
{

// simple class to enforce clean-up of PNG structures
struct auto_png
{
  auto_png(png_structp& _png, png_infop& _info)
  : png(_png),
    info(_info)
  {
  }

  ~auto_png()
  {
    if(NULL != png)
    {
      png_destroy_read_struct(&png, &info, NULL);
    }
  }

  png_structp& png;
  png_infop& info;
}; // struct auto_png;

bool LoadPngHeader(FILE *fp, unsigned int &width, unsigned int &height, png_structp &png, png_infop &info)
{
  png_byte header[8] = { 0 };

  // Check header to see if it is a PNG file
  size_t size = fread(header, 1, 8, fp);
  if(size != 8)
  {
    return false;
  }

  if(png_sig_cmp(header, 0, 8))
  {
    return false;
  }

  png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

  if(!png)
  {
    DALI_LOG_WARNING("Can't create PNG read structure\n");
    return false;
  }

  info = png_create_info_struct(png);
  if(!info)
  {
    DALI_LOG_WARNING("png_create_info_struct failed\n");
    return false;
  }

  png_set_expand(png);

  if(setjmp(png_jmpbuf(png)))
  {
    DALI_LOG_WARNING("error during png_init_io\n");
    return false;
  }

  png_init_io(png, fp);
  png_set_sig_bytes(png, 8);

  // read image info
  png_read_info(png, info);

  // dimensions
  width = png_get_image_width(png, info);
  height = png_get_image_height(png, info);

  return true;
}

} // namespace - anonymous

bool LoadPngHeader( const ImageLoader::Input& input, unsigned int& width, unsigned int& height )
{
  png_structp png = NULL;
  png_infop info = NULL;
  auto_png autoPng(png, info);

  bool success = LoadPngHeader( input.file, width, height, png, info );

  return success;
}

bool LoadBitmapFromPng( const ImageLoader::Input& input, Integration::Bitmap& bitmap )
{
  png_structp png = NULL;
  png_infop info = NULL;
  auto_png autoPng(png, info);

  /// @todo: consider parameters
  unsigned int y;
  unsigned int width, height;
  unsigned char *pixels;
  png_bytep *rows;
  unsigned int bpp = 0; // bytes per pixel
  bool valid = false;

  // Load info from the header
  if( !LoadPngHeader( input.file, width, height, png, info ) )
  {
    return false;
  }

  Pixel::Format pixelFormat = Pixel::RGBA8888;

  // decide pixel format
  unsigned int colordepth = png_get_bit_depth(png, info);

  // Ask PNGLib to convert high precision images into something we can use:
  if (colordepth == 16)
  {
    png_set_strip_16(png);
    colordepth = 8;
  }

  png_byte colortype = png_get_color_type(png, info);

  if( colortype == PNG_COLOR_TYPE_GRAY ||
      colortype == PNG_COLOR_TYPE_GRAY_ALPHA )
  {
    if( png_get_valid(png, info, PNG_INFO_tRNS) )
    {
      colortype = PNG_COLOR_TYPE_GRAY_ALPHA;
      /* expand transparency entry -> alpha channel if present */
      png_set_tRNS_to_alpha(png);
      pixelFormat = Pixel::LA88;
    }
    else
    {
      colortype = PNG_COLOR_TYPE_GRAY;
      pixelFormat = Pixel::L8;
    }

    if( colordepth < 8 )
    {
      /* expand gray (w/reduced bits) -> 8-bit RGB if necessary */
      png_set_expand_gray_1_2_4_to_8(png);
      /* pack all pixels to byte boundaries */
      png_set_packing(png);
    }
    valid = true;
  }
  else if(colortype == PNG_COLOR_TYPE_RGB )
  {
    switch(colordepth)
    {
      case 8:
      {
        pixelFormat = Pixel::RGB888;
        valid = true;
        break;
      }
      case 5:      /// @todo is this correct for RGB16 5-6-5 ?
      {
        pixelFormat = Pixel::RGB565;
        valid = true;
        break;
      }
      default:
      {
        break;
      }
    }
  }
  else if(colortype == PNG_COLOR_TYPE_RGBA)
  {
    switch(colordepth)
    {
      case 8:
      {
        pixelFormat = Pixel::RGBA8888;
        valid = true;
        break;
      }
      default:
      {
        break;
      }
    }
  }
  else if(colortype == PNG_COLOR_TYPE_PALETTE)
  {
    switch(colordepth)
    {
      case 1:
      {
        pixelFormat = Pixel::LA88;
        valid = true;
        break;
      }

      case 2:
      case 4:
      case 8:
      {
        /* Expand paletted or RGB images with transparency to full alpha channels
         * so the data will be available as RGBA quartets. PNG_INFO_tRNS = 0x10
         */
        if(png_get_valid(png, info, PNG_INFO_tRNS) == 0x10)
        {
          pixelFormat = Pixel::RGBA8888;
          valid = true;
        }
        else
        {
          pixelFormat = Pixel::RGB888;
          png_set_packing(png);
          png_set_packswap(png);
          png_set_palette_to_rgb(png);
          valid = true;
        }
        break;
      }
      default:
      {
        break;
      }
    }
  }

  if( !valid )
  {
    DALI_LOG_WARNING( "Unsupported png format\n" );
    return false;
  }

  // bytes per pixel
  bpp = Pixel::GetBytesPerPixel(pixelFormat);

  png_read_update_info(png, info);

  if(setjmp(png_jmpbuf(png)))
  {
    DALI_LOG_WARNING("error during png_read_image\n");
    return false;
  }

  unsigned int rowBytes = png_get_rowbytes(png, info);

  unsigned int bufferWidth   = GetTextureDimension(width);
  unsigned int bufferHeight  = GetTextureDimension(height);
  unsigned int stride        = bufferWidth*bpp;

  // not sure if this ever happens
  if( rowBytes > stride )
  {
    stride = GetTextureDimension(rowBytes);

    bpp = stride / bufferWidth;
    switch(bpp)
    {
      case 3:
        pixelFormat = Pixel::RGB888;
        break;
      case 4:
        pixelFormat = Pixel::RGBA8888;
        break;
      default:
        break;
    }

  }

  // decode the whole image into bitmap buffer
  pixels = bitmap.GetPackedPixelsProfile()->ReserveBuffer(pixelFormat, width, height, bufferWidth, bufferHeight);

  DALI_ASSERT_DEBUG(pixels);
  rows = reinterpret_cast< png_bytep* >( malloc(sizeof(png_bytep) * height) );
  for(y=0; y<height; y++)
  {
    rows[y] = pixels + y * stride;
  }

  // decode image
  png_read_image(png, rows);

  free(rows);

  return true;
}

// simple class to enforce clean-up of PNG structures
struct AutoPngWrite
{
  AutoPngWrite(png_structp& _png, png_infop& _info)
  : png(_png),
    info(_info)
  {
  }

  ~AutoPngWrite()
  {
    if(NULL != png)
    {
      png_destroy_write_struct(&png, &info);
    }
  }

  png_structp& png;
  png_infop& info;
}; // struct AutoPngWrite;

namespace
{
  // Custom libpng write callbacks that buffer to a vector instead of a file:

  /**
   * extern "C" linkage is used because this is a callback that we pass to a C
   * library which is part of the underlying platform and so potentially compiled
   * as C rather than C++.
   * @see http://stackoverflow.com/a/2594222
   * */
  extern "C" void WriteData(png_structp png_ptr, png_bytep data, png_size_t length)
  {
    DALI_ASSERT_DEBUG(png_ptr && data);
    if(!png_ptr || !data)
    {
      return;
    }
    // Make sure we don't try to propagate a C++ exception up the call stack of a pure C library:
    try
    {
      // Recover our buffer for writing into:
      Vector<unsigned char>* const encoded_img = static_cast< Vector<unsigned char>* >( png_get_io_ptr(png_ptr) );
      if(encoded_img)
      {
        const Vector<unsigned char>::SizeType bufferSize = encoded_img->Count();
        encoded_img->Resize( bufferSize + length ); //< Can throw OOM.
        unsigned char* const bufferBack = encoded_img->Begin() + bufferSize;
        memcpy(bufferBack, data, length);
      }
      else
      {
        DALI_LOG_ERROR("PNG buffer for write to memory was passed from libpng as null.\n");
      }
    }
    catch(...)
    {
      DALI_LOG_ERROR("C++ Exception caught\n");
    }
  }

  /** Override the flush with a NOP to prevent libpng trying cstdlib file io. */
  extern "C" void FlushData(png_structp png_ptr)
  {
#ifdef DEBUG_ENABLED
    Debug::LogMessage(Debug::DebugInfo, "PNG Flush");
#endif // DEBUG_ENABLED
  }
}

/**
 * Potential improvements:
 * 1. Detect <= 256 colours and write in palette mode.
 * 2. Detect grayscale (will early-out quickly for colour images).
 * 3. Store colour space / gamma correction info related to the device screen?
 *    http://www.libpng.org/pub/png/book/chapter10.html
 * 4. Refactor with callers to write straight through to disk and save keeping a big buffer around.
 * 5. Prealloc buffer (reserve) to input size / <A number greater than 2 (expexcted few realloc but without using lots of memory) | 1 (expected zero reallocs but using a lot of memory)>.
 * 6. Set the modification time with png_set_tIME(png_ptr, info_ptr, mod_time);
 * 7. If caller asks for no compression, bypass libpng and blat raw data to
 *    disk, topped and tailed with header/tail blocks.
 */
bool EncodeToPng( const unsigned char* const pixelBuffer, Vector<unsigned char>& encodedPixels, std::size_t width, std::size_t height, Pixel::Format pixelFormat )
{
  // Translate pixel format enum:
  int pngPixelFormat = -1;
  unsigned pixelBytes = 0;
  bool rgbaOrder = true;

  // Account for RGB versus BGR and presence of alpha in input pixels:
  switch( pixelFormat )
  {
    case Pixel::RGB888:
    {
      pngPixelFormat = PNG_COLOR_TYPE_RGB;
      pixelBytes = 3;
      break;
    }
    case Pixel::BGRA8888:
    {
      rgbaOrder = false;
      ///! No break: fall through:
    }
    case Pixel::RGBA8888:
    {
      pngPixelFormat = PNG_COLOR_TYPE_RGB_ALPHA;
      pixelBytes = 4;
      break;
    }
    default:
    {
      DALI_LOG_ERROR( "Unsupported pixel format for encoding to PNG.\n" );
      return false;
    }
  }

  const int interlace = PNG_INTERLACE_NONE;

  png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if(!png_ptr)
  {
    return false;
  }
  /* Allocate/initialize the image information data.  REQUIRED */
  png_infop info_ptr = png_create_info_struct( png_ptr );
  if(!info_ptr)
  {
    png_destroy_write_struct(&png_ptr, NULL);
    return false;
  }

  /* Set error handling.  REQUIRED if you aren't supplying your own
   * error handling functions in the png_create_write_struct() call.
   */
  if(setjmp(png_jmpbuf(png_ptr)))
  {
    png_destroy_write_struct(&png_ptr, &info_ptr);
    return false;
  }

  // Since we are going to write to memory instead of a file, lets provide
  // libpng with a custom write function and ask it to pass back our
  // Vector buffer each time it calls back to flush data to "file":
  png_set_write_fn(png_ptr, &encodedPixels, WriteData, FlushData);

  // png_set_compression_level( png_ptr, Z_BEST_COMPRESSION);
  png_set_compression_level(png_ptr, Z_BEST_SPEED);
  // png_set_compression_level( png_ptr, Z_NO_COMPRESSION); //! We could just generate png directly without libpng in this case.

  // Explicitly limit the number of filters used per scanline to speed us up:
  // png_set_filter(png_ptr, 0, PNG_FILTER_NONE); ///!ToDo: Try this once baseline profile is in place.
       // PNG_FILTER_SUB   |
       // PNG_FILTER_UP    |
       // PNG_FILTER_AVE   |
       // PNG_FILTER_PAETH |
       // PNG_ALL_FILTERS);
  // Play with Zlib parameters in optimisation phase:
    // png_set_compression_mem_level(png_ptr, 8);
    // png_set_compression_strategy(png_ptr,
        // Z_DEFAULT_STRATEGY);
    // png_set_compression_window_bits(png_ptr, 15);
    // png_set_compression_method(png_ptr, 8);
    // png_set_compression_buffer_size(png_ptr, 8192)

  // Let lib_png know if the pixel bytes are in BGR(A) order:
  if(!rgbaOrder)
  {
    png_set_bgr( png_ptr );
  }

  // Set the image information:
  png_set_IHDR(png_ptr, info_ptr, width, height, 8,
     pngPixelFormat, interlace,
     PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

  // Start to output the PNG data to our buffer:
  png_write_info(png_ptr, info_ptr);

  // Walk the rows:
  const unsigned row_step = width * pixelBytes;
  png_bytep row_ptr = const_cast<png_bytep>(pixelBuffer);
  const png_bytep row_end = row_ptr + height * row_step;
  for(; row_ptr < row_end; row_ptr += row_step)
  {
    png_write_row(png_ptr, row_ptr);
  }

  /* It is REQUIRED to call this to finish writing the rest of the file */
  png_write_end(png_ptr, info_ptr);
  /* Clean up after the write, and free any memory allocated */
  png_destroy_write_struct(&png_ptr, &info_ptr);
  return true;
}

} // namespace TizenPlatform

} // namespace Dali
<commit_msg>fixed bug about PNG_COLOR_TYPE_GRAY_ALPHA<commit_after>/*
 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
 *
 * 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.
 *
 */

#include "loader-png.h"

#include <cstring>
#include <cstdlib>

#include <zlib.h>
#include <png.h>

#include <dali/integration-api/bitmap.h>
#include <dali/integration-api/debug.h>
#include <dali/public-api/images/image.h>
#include "dali/public-api/math/math-utils.h"
#include "dali/public-api/math/vector2.h"
#include "platform-capabilities.h"

namespace Dali
{

using Integration::Bitmap;
using Dali::Integration::PixelBuffer;

namespace TizenPlatform
{

namespace
{

// simple class to enforce clean-up of PNG structures
struct auto_png
{
  auto_png(png_structp& _png, png_infop& _info)
  : png(_png),
    info(_info)
  {
  }

  ~auto_png()
  {
    if(NULL != png)
    {
      png_destroy_read_struct(&png, &info, NULL);
    }
  }

  png_structp& png;
  png_infop& info;
}; // struct auto_png;

bool LoadPngHeader(FILE *fp, unsigned int &width, unsigned int &height, png_structp &png, png_infop &info)
{
  png_byte header[8] = { 0 };

  // Check header to see if it is a PNG file
  size_t size = fread(header, 1, 8, fp);
  if(size != 8)
  {
    return false;
  }

  if(png_sig_cmp(header, 0, 8))
  {
    return false;
  }

  png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

  if(!png)
  {
    DALI_LOG_WARNING("Can't create PNG read structure\n");
    return false;
  }

  info = png_create_info_struct(png);
  if(!info)
  {
    DALI_LOG_WARNING("png_create_info_struct failed\n");
    return false;
  }

  png_set_expand(png);

  if(setjmp(png_jmpbuf(png)))
  {
    DALI_LOG_WARNING("error during png_init_io\n");
    return false;
  }

  png_init_io(png, fp);
  png_set_sig_bytes(png, 8);

  // read image info
  png_read_info(png, info);

  // dimensions
  width = png_get_image_width(png, info);
  height = png_get_image_height(png, info);

  return true;
}

} // namespace - anonymous

bool LoadPngHeader( const ImageLoader::Input& input, unsigned int& width, unsigned int& height )
{
  png_structp png = NULL;
  png_infop info = NULL;
  auto_png autoPng(png, info);

  bool success = LoadPngHeader( input.file, width, height, png, info );

  return success;
}

bool LoadBitmapFromPng( const ImageLoader::Input& input, Integration::Bitmap& bitmap )
{
  png_structp png = NULL;
  png_infop info = NULL;
  auto_png autoPng(png, info);

  /// @todo: consider parameters
  unsigned int y;
  unsigned int width, height;
  unsigned char *pixels;
  png_bytep *rows;
  unsigned int bpp = 0; // bytes per pixel
  bool valid = false;

  // Load info from the header
  if( !LoadPngHeader( input.file, width, height, png, info ) )
  {
    return false;
  }

  Pixel::Format pixelFormat = Pixel::RGBA8888;

  // decide pixel format
  unsigned int colordepth = png_get_bit_depth(png, info);

  // Ask PNGLib to convert high precision images into something we can use:
  if (colordepth == 16)
  {
    png_set_strip_16(png);
    colordepth = 8;
  }

  png_byte colortype = png_get_color_type(png, info);

  if( colortype == PNG_COLOR_TYPE_GRAY ||
      colortype == PNG_COLOR_TYPE_GRAY_ALPHA )
  {
    if( colortype == PNG_COLOR_TYPE_GRAY )
    {
      pixelFormat = Pixel::L8;
      if( png_get_valid(png, info, PNG_INFO_tRNS) )
      {
        colortype = PNG_COLOR_TYPE_GRAY_ALPHA;
        /* expand transparency entry -> alpha channel if present */
        png_set_tRNS_to_alpha(png);
        pixelFormat = Pixel::LA88;
      }
    }
    else
    {
      pixelFormat = Pixel::LA88;
    }

    if( colordepth < 8 )
    {
      /* expand gray (w/reduced bits) -> 8-bit RGB if necessary */
      png_set_expand_gray_1_2_4_to_8(png);
      /* pack all pixels to byte boundaries */
      png_set_packing(png);
    }
    valid = true;
  }
  else if(colortype == PNG_COLOR_TYPE_RGB )
  {
    switch(colordepth)
    {
      case 8:
      {
        pixelFormat = Pixel::RGB888;
        valid = true;
        break;
      }
      case 5:      /// @todo is this correct for RGB16 5-6-5 ?
      {
        pixelFormat = Pixel::RGB565;
        valid = true;
        break;
      }
      default:
      {
        break;
      }
    }
  }
  else if(colortype == PNG_COLOR_TYPE_RGBA)
  {
    switch(colordepth)
    {
      case 8:
      {
        pixelFormat = Pixel::RGBA8888;
        valid = true;
        break;
      }
      default:
      {
        break;
      }
    }
  }
  else if(colortype == PNG_COLOR_TYPE_PALETTE)
  {
    switch(colordepth)
    {
      case 1:
      {
        pixelFormat = Pixel::LA88;
        valid = true;
        break;
      }

      case 2:
      case 4:
      case 8:
      {
        /* Expand paletted or RGB images with transparency to full alpha channels
         * so the data will be available as RGBA quartets. PNG_INFO_tRNS = 0x10
         */
        if(png_get_valid(png, info, PNG_INFO_tRNS) == 0x10)
        {
          pixelFormat = Pixel::RGBA8888;
          valid = true;
        }
        else
        {
          pixelFormat = Pixel::RGB888;
          png_set_packing(png);
          png_set_packswap(png);
          png_set_palette_to_rgb(png);
          valid = true;
        }
        break;
      }
      default:
      {
        break;
      }
    }
  }

  if( !valid )
  {
    DALI_LOG_WARNING( "Unsupported png format\n" );
    return false;
  }

  // bytes per pixel
  bpp = Pixel::GetBytesPerPixel(pixelFormat);

  png_read_update_info(png, info);

  if(setjmp(png_jmpbuf(png)))
  {
    DALI_LOG_WARNING("error during png_read_image\n");
    return false;
  }

  unsigned int rowBytes = png_get_rowbytes(png, info);

  unsigned int bufferWidth   = GetTextureDimension(width);
  unsigned int bufferHeight  = GetTextureDimension(height);
  unsigned int stride        = bufferWidth*bpp;

  // not sure if this ever happens
  if( rowBytes > stride )
  {
    stride = GetTextureDimension(rowBytes);

    bpp = stride / bufferWidth;
    switch(bpp)
    {
      case 3:
        pixelFormat = Pixel::RGB888;
        break;
      case 4:
        pixelFormat = Pixel::RGBA8888;
        break;
      default:
        break;
    }

  }

  // decode the whole image into bitmap buffer
  pixels = bitmap.GetPackedPixelsProfile()->ReserveBuffer(pixelFormat, width, height, bufferWidth, bufferHeight);

  DALI_ASSERT_DEBUG(pixels);
  rows = reinterpret_cast< png_bytep* >( malloc(sizeof(png_bytep) * height) );
  for(y=0; y<height; y++)
  {
    rows[y] = pixels + y * stride;
  }

  // decode image
  png_read_image(png, rows);

  free(rows);

  return true;
}

// simple class to enforce clean-up of PNG structures
struct AutoPngWrite
{
  AutoPngWrite(png_structp& _png, png_infop& _info)
  : png(_png),
    info(_info)
  {
  }

  ~AutoPngWrite()
  {
    if(NULL != png)
    {
      png_destroy_write_struct(&png, &info);
    }
  }

  png_structp& png;
  png_infop& info;
}; // struct AutoPngWrite;

namespace
{
  // Custom libpng write callbacks that buffer to a vector instead of a file:

  /**
   * extern "C" linkage is used because this is a callback that we pass to a C
   * library which is part of the underlying platform and so potentially compiled
   * as C rather than C++.
   * @see http://stackoverflow.com/a/2594222
   * */
  extern "C" void WriteData(png_structp png_ptr, png_bytep data, png_size_t length)
  {
    DALI_ASSERT_DEBUG(png_ptr && data);
    if(!png_ptr || !data)
    {
      return;
    }
    // Make sure we don't try to propagate a C++ exception up the call stack of a pure C library:
    try
    {
      // Recover our buffer for writing into:
      Vector<unsigned char>* const encoded_img = static_cast< Vector<unsigned char>* >( png_get_io_ptr(png_ptr) );
      if(encoded_img)
      {
        const Vector<unsigned char>::SizeType bufferSize = encoded_img->Count();
        encoded_img->Resize( bufferSize + length ); //< Can throw OOM.
        unsigned char* const bufferBack = encoded_img->Begin() + bufferSize;
        memcpy(bufferBack, data, length);
      }
      else
      {
        DALI_LOG_ERROR("PNG buffer for write to memory was passed from libpng as null.\n");
      }
    }
    catch(...)
    {
      DALI_LOG_ERROR("C++ Exception caught\n");
    }
  }

  /** Override the flush with a NOP to prevent libpng trying cstdlib file io. */
  extern "C" void FlushData(png_structp png_ptr)
  {
#ifdef DEBUG_ENABLED
    Debug::LogMessage(Debug::DebugInfo, "PNG Flush");
#endif // DEBUG_ENABLED
  }
}

/**
 * Potential improvements:
 * 1. Detect <= 256 colours and write in palette mode.
 * 2. Detect grayscale (will early-out quickly for colour images).
 * 3. Store colour space / gamma correction info related to the device screen?
 *    http://www.libpng.org/pub/png/book/chapter10.html
 * 4. Refactor with callers to write straight through to disk and save keeping a big buffer around.
 * 5. Prealloc buffer (reserve) to input size / <A number greater than 2 (expexcted few realloc but without using lots of memory) | 1 (expected zero reallocs but using a lot of memory)>.
 * 6. Set the modification time with png_set_tIME(png_ptr, info_ptr, mod_time);
 * 7. If caller asks for no compression, bypass libpng and blat raw data to
 *    disk, topped and tailed with header/tail blocks.
 */
bool EncodeToPng( const unsigned char* const pixelBuffer, Vector<unsigned char>& encodedPixels, std::size_t width, std::size_t height, Pixel::Format pixelFormat )
{
  // Translate pixel format enum:
  int pngPixelFormat = -1;
  unsigned pixelBytes = 0;
  bool rgbaOrder = true;

  // Account for RGB versus BGR and presence of alpha in input pixels:
  switch( pixelFormat )
  {
    case Pixel::RGB888:
    {
      pngPixelFormat = PNG_COLOR_TYPE_RGB;
      pixelBytes = 3;
      break;
    }
    case Pixel::BGRA8888:
    {
      rgbaOrder = false;
      ///! No break: fall through:
    }
    case Pixel::RGBA8888:
    {
      pngPixelFormat = PNG_COLOR_TYPE_RGB_ALPHA;
      pixelBytes = 4;
      break;
    }
    default:
    {
      DALI_LOG_ERROR( "Unsupported pixel format for encoding to PNG.\n" );
      return false;
    }
  }

  const int interlace = PNG_INTERLACE_NONE;

  png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if(!png_ptr)
  {
    return false;
  }
  /* Allocate/initialize the image information data.  REQUIRED */
  png_infop info_ptr = png_create_info_struct( png_ptr );
  if(!info_ptr)
  {
    png_destroy_write_struct(&png_ptr, NULL);
    return false;
  }

  /* Set error handling.  REQUIRED if you aren't supplying your own
   * error handling functions in the png_create_write_struct() call.
   */
  if(setjmp(png_jmpbuf(png_ptr)))
  {
    png_destroy_write_struct(&png_ptr, &info_ptr);
    return false;
  }

  // Since we are going to write to memory instead of a file, lets provide
  // libpng with a custom write function and ask it to pass back our
  // Vector buffer each time it calls back to flush data to "file":
  png_set_write_fn(png_ptr, &encodedPixels, WriteData, FlushData);

  // png_set_compression_level( png_ptr, Z_BEST_COMPRESSION);
  png_set_compression_level(png_ptr, Z_BEST_SPEED);
  // png_set_compression_level( png_ptr, Z_NO_COMPRESSION); //! We could just generate png directly without libpng in this case.

  // Explicitly limit the number of filters used per scanline to speed us up:
  // png_set_filter(png_ptr, 0, PNG_FILTER_NONE); ///!ToDo: Try this once baseline profile is in place.
       // PNG_FILTER_SUB   |
       // PNG_FILTER_UP    |
       // PNG_FILTER_AVE   |
       // PNG_FILTER_PAETH |
       // PNG_ALL_FILTERS);
  // Play with Zlib parameters in optimisation phase:
    // png_set_compression_mem_level(png_ptr, 8);
    // png_set_compression_strategy(png_ptr,
        // Z_DEFAULT_STRATEGY);
    // png_set_compression_window_bits(png_ptr, 15);
    // png_set_compression_method(png_ptr, 8);
    // png_set_compression_buffer_size(png_ptr, 8192)

  // Let lib_png know if the pixel bytes are in BGR(A) order:
  if(!rgbaOrder)
  {
    png_set_bgr( png_ptr );
  }

  // Set the image information:
  png_set_IHDR(png_ptr, info_ptr, width, height, 8,
     pngPixelFormat, interlace,
     PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

  // Start to output the PNG data to our buffer:
  png_write_info(png_ptr, info_ptr);

  // Walk the rows:
  const unsigned row_step = width * pixelBytes;
  png_bytep row_ptr = const_cast<png_bytep>(pixelBuffer);
  const png_bytep row_end = row_ptr + height * row_step;
  for(; row_ptr < row_end; row_ptr += row_step)
  {
    png_write_row(png_ptr, row_ptr);
  }

  /* It is REQUIRED to call this to finish writing the rest of the file */
  png_write_end(png_ptr, info_ptr);
  /* Clean up after the write, and free any memory allocated */
  png_destroy_write_struct(&png_ptr, &info_ptr);
  return true;
}

} // namespace TizenPlatform

} // namespace Dali
<|endoftext|>


<commit_before>/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://opensource.org/licenses/BSD-3-Clause for
   details.  */

#include <functional>

#include "helpers.h"
#include "transform.h"

using namespace std::placeholders;

static void run(std::vector<Command> &commands, std::vector<Span> &spans,
                const std::string &argument_indent_string) {

    for (auto c : commands) {
        const std::string ident = lowerstring(spans[c.identifier].data);

        std::string command_indentation;
        if (spans[c.identifier - 1].type == SpanType::Newline) {
            command_indentation = "";
        } else if (spans[c.identifier - 1].type == SpanType::Space) {
            command_indentation = spans[c.identifier - 1].data;
        } else {
            throw std::runtime_error("command '" + ident + "' not preceded by space or newline: '" +
                                     spans[c.identifier - 1].data + "'");
        }

        // Walk forwards to fix argument indents.
        size_t next_token = c.identifier + 1;
        while (spans[next_token].type != SpanType::Lparen) {
            next_token++;
        }
        next_token++;
        while (spans[next_token].type != SpanType::Rparen) {
            if (spans[next_token].type == SpanType::Space) {
                spans[next_token].data = "";
            } else if (spans[next_token].type == SpanType::Newline) {
                spans[next_token].data = "";
            } else {
                if (spans[next_token - 1].type != SpanType::Space) {
                    spans.insert(spans.begin() + next_token, Span{SpanType::Space, ""});
                    next_token++;
                    // Fix up commands
                    for (auto &c : commands) {
                        if (c.identifier >= next_token) {
                            c.identifier++;
                        }
                    }
                }
                spans[next_token - 1].data = "\n" + command_indentation + argument_indent_string;
            }
            next_token++;
        }
        if (spans[next_token - 1].type != SpanType::Space) {
            spans.insert(spans.begin() + next_token, Span{SpanType::Space, ""});
            next_token++;
            // Fix up commands
            for (auto &c : commands) {
                if (c.identifier >= next_token) {
                    c.identifier++;
                }
            }
        }
        spans[next_token - 1].data = "\n" + command_indentation;
    }
}

static bool handleCommandLine(const std::string &arg,
                              std::vector<TransformFunction> &transform_functions) {
    if (arg.find("-argument-per-line=") != 0) {
        return false;
    }

    std::string indent_string = arg.substr(std::string{"-argument-per-line="}.size());
    replace_all_in_string(indent_string, "\\t", "\t");
    transform_functions.emplace_back(std::bind(run, _1, _2, indent_string));

    return true;
};

static const on_program_load transform_argument_per_line{[]() {
    getCommandLineDescriptions().emplace_back(
        "-argument-per-line=STRING", "Put each argument on its own line, indented by STRING.");
    getCommandLineHandlers().emplace_back(&handleCommandLine);
}};

TEST_CASE("Puts each argument on its own line", "[transform.argument_per_line]") {
    REQUIRE_TRANSFORMS_TO(std::bind(run, _1, _2, "INDENT "),
                          R"(
    command(ARG1 ARG2 ARG3)
    cmake_minimum_required(VERSION 3.0)
    project(cmake-format)
)",
                          R"(
    command(
    INDENT ARG1
    INDENT ARG2
    INDENT ARG3
    )
    cmake_minimum_required(
    INDENT VERSION
    INDENT 3.0
    )
    project(
    INDENT cmake-format
    )
)");
}
<commit_msg>transform_argument_per_line: use new helpers<commit_after>/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://opensource.org/licenses/BSD-3-Clause for
   details.  */

#include <functional>

#include "helpers.h"
#include "transform.h"

using namespace std::placeholders;

static void run(std::vector<Command> &commands, std::vector<Span> &spans,
                const std::string &argument_indent_string) {

    for (auto c : commands) {
        std::string command_indentation = get_command_indentation(c.identifier, spans);

        // Walk forwards to fix argument indents.
        size_t next_token = c.identifier + 1;
        while (spans[next_token].type != SpanType::Lparen) {
            next_token++;
        }
        next_token++;
        while (spans[next_token].type != SpanType::Rparen) {
            if (spans[next_token].type == SpanType::Space) {
                delete_span(commands, spans, next_token);
            } else if (spans[next_token].type == SpanType::Newline) {
                delete_span(commands, spans, next_token);
            } else {
                insert_span_before(
                    next_token, commands, spans,
                    {{SpanType::Newline, "\n"},
                     {SpanType::Space, command_indentation + argument_indent_string}});
                next_token++;
            }
        }
        insert_span_before(next_token, commands, spans,
                           {{SpanType::Newline, "\n"}, {SpanType::Space, command_indentation}});
    }
}

static bool handleCommandLine(const std::string &arg,
                              std::vector<TransformFunction> &transform_functions) {
    if (arg.find("-argument-per-line=") != 0) {
        return false;
    }

    std::string indent_string = arg.substr(std::string{"-argument-per-line="}.size());
    replace_all_in_string(indent_string, "\\t", "\t");
    transform_functions.emplace_back(std::bind(run, _1, _2, indent_string));

    return true;
};

static const on_program_load transform_argument_per_line{[]() {
    getCommandLineDescriptions().emplace_back(
        "-argument-per-line=STRING", "Put each argument on its own line, indented by STRING.");
    getCommandLineHandlers().emplace_back(&handleCommandLine);
}};

TEST_CASE("Puts each argument on its own line", "[transform.argument_per_line]") {
    REQUIRE_TRANSFORMS_TO(std::bind(run, _1, _2, "INDENT "),
                          R"(
    command(ARG1 ARG2 ARG3)
    cmake_minimum_required(VERSION 3.0)
    project(cmake-format)
)",
                          R"(
    command(
    INDENT ARG1
    INDENT ARG2
    INDENT ARG3
    )
    cmake_minimum_required(
    INDENT VERSION
    INDENT 3.0
    )
    project(
    INDENT cmake-format
    )
)");
}
<|endoftext|>


<commit_before>#include "menubar.h"
#include <QDebug>
#include <QFontDatabase>
#include <QFont>
#include <QColor>
#include <QFontMetrics>

MenuBar::MenuBar(QWidget *parent): QMenuBar(parent)
{

  int font_id = QFontDatabase::addApplicationFont("://resources/DejaVuSans-ExtraLight.ttf");
  QString family = QFontDatabase::applicationFontFamilies(font_id).at(0);

  _font.setFamily(family);
  _font.setStyleStrategy(QFont::PreferAntialias);
  _font.setPixelSize(12);

  QFontMetrics fm(_font);
  _width_plot = fm.width("Plot");
  _width_juggler = fm.width("Juggler");
}

void MenuBar::paintEvent(QPaintEvent *event)
{
  QMenuBar::paintEvent(event);
  QPainter painter(this);
  painter.setFont( _font );

  int text_width = _width_plot + _width_juggler;
  {
    QPoint topleft(this->rect().width() - text_width - 12, 0);
    QSize rect_size(_width_plot, this->rect().height());
    painter.setPen(QColor("#ce0e73"));
    painter.drawText(QRect(topleft, rect_size), Qt::AlignHCenter | Qt::AlignVCenter, "Plot");
  }
  {
    QPoint topleft(this->rect().width() - _width_juggler - 10, 0);
    QSize rect_size(_width_juggler, this->rect().height());
    painter.setPen(QColor("#1b72cf"));
    painter.drawText(QRect(topleft, rect_size), Qt::AlignHCenter | Qt::AlignVCenter, "Juggler");
  }
}

<commit_msg>fix menu bar size of PlotJuggler<commit_after>#include "menubar.h"
#include <QDebug>
#include <QFontDatabase>
#include <QFont>
#include <QColor>
#include <QFontMetrics>

MenuBar::MenuBar(QWidget *parent): QMenuBar(parent)
{

  int font_id = QFontDatabase::addApplicationFont("://resources/DejaVuSans-ExtraLight.ttf");
  QString family = QFontDatabase::applicationFontFamilies(font_id).at(0);

  _font.setFamily(family);
  _font.setStyleStrategy(QFont::PreferAntialias);
  _font.setPixelSize(18);

  QFontMetrics fm(_font);
  _width_plot = fm.width("Plot");
  _width_juggler = fm.width("Juggler");
}

void MenuBar::paintEvent(QPaintEvent *event)
{
  QMenuBar::paintEvent(event);
  QPainter painter(this);
  painter.setFont( _font );

  int text_width = _width_plot + _width_juggler;
  {
    QPoint topleft(this->rect().width() - text_width - 12, 0);
    QSize rect_size(_width_plot, this->rect().height());
    painter.setPen(QColor("#ce0e73"));
    painter.drawText(QRect(topleft, rect_size), Qt::AlignHCenter | Qt::AlignVCenter, "Plot");
  }
  {
    QPoint topleft(this->rect().width() - _width_juggler - 10, 0);
    QSize rect_size(_width_juggler, this->rect().height());
    painter.setPen(QColor("#1b72cf"));
    painter.drawText(QRect(topleft, rect_size), Qt::AlignHCenter | Qt::AlignVCenter, "Juggler");
  }
}

<|endoftext|>


<commit_before>#include "scene.h"

#include <algorithm>

#include "util/stream_puper.h"
#include "type_manager.h"
#include "event_manager.h"
#include "file_manager.h"
#include "scene_events.h"
#include "component_storage.h"

namespace oak {

	EntityId Scene::createEntity() {
		EntityId id;
		if (freeIndices_.empty()) {
			id = { static_cast<uint32_t>(generations_.size()), 0 };
			generations_.push_back(0);
		} else {
			uint32_t i = freeIndices_.front();
			id = { i, generations_[i] };
			freeIndices_.pop_front();
		}

		ensureSize(id.index);

		entities_.push_back(id);
		EventManager::inst().getQueue<EntityCreateEvent>().emit({ id });
		return id;
	}

	void Scene::destroyEntity(EntityId entity) {
		deactivateEntity(entity);
		generations_[entity]++;
		killed_.push_back(entity);
		EventManager::inst().getQueue<EntityDestroyEvent>().emit({ entity });
	}

	void Scene::activateEntity(EntityId entity) {
		flags_[entity][0] = true;
		EventManager::inst().getQueue<EntityActivateEvent>().emit({ entity });
	}

	void Scene::deactivateEntity(EntityId entity) {
		flags_[entity][0] = false;
		EventManager::inst().getQueue<EntityDeactivateEvent>().emit({ entity });
	}

	bool Scene::isEntityAlive(EntityId entity) const {
		return generations_.size() > entity.index ? generations_[entity] == entity.generation : false;
	}

	bool Scene::isEntityActive(EntityId entity) const {
		return flags_.size() > entity.index ? flags_[entity][0] : false;
	}

	void* Scene::addComponent(EntityId entity, size_t tid) {
		componentMasks_[entity][tid] = true;
		return componentPools_[tid]->addComponent(entity);
	}

	void Scene::addComponent(EntityId entity, size_t tid, const void *ptr) {
		componentMasks_[entity][tid] = true;
		componentPools_[tid]->addComponent(entity, ptr);
	}

	void Scene::removeComponent(EntityId entity, size_t tid) {
		componentMasks_[entity][tid] = false;
		componentPools_[tid]->removeComponent(entity);
	}

	void* Scene::getComponent(EntityId entity, size_t tid) {
		return componentPools_[tid]->getComponent(entity);
	}

	const void* Scene::getComponent(EntityId entity, size_t tid) const {
		return componentPools_[tid]->getComponent(entity);
	}

	bool Scene::hasComponent(EntityId entity, size_t tid) const {
		return componentMasks_[entity][tid];
	}

	const std::bitset<config::MAX_COMPONENTS>& Scene::getComponentFilter(EntityId entity) const {
		return componentMasks_[entity];
	}

	void Scene::init() {
		//create component storages for types in ComponentTypeManager
		for (auto it : ComponentTypeManager::inst().getTypes()) {
			auto ptr = static_cast<ComponentStorage*>(oak_allocator.allocate(sizeof(ComponentStorage)));
			new (ptr) ComponentStorage(it);
			ownsPools_.push_back(ptr);
			addComponentStorage(ptr);
		}
	}

	void Scene::terminate() {
		reset();
		for (auto it : ownsPools_) {
			it->~ComponentStorage();
			oak_allocator.deallocate(it, sizeof(ComponentStorage));
		}
		ownsPools_.clear();
	}

	void Scene::update() {
		for (const auto& e : killed_) {
			//remove all the entities components
			removeAllComponents(e);
			//remove the entity from the alive list and recycle the index
			entities_.erase(std::remove(std::begin(entities_), std::end(entities_), e), std::end(entities_));
			freeIndices_.push_back(e);
		}

		killed_.clear();
	}

	void Scene::reset() {
		auto& deactivateQueue = EventManager::inst().getQueue<EntityDeactivateEvent>();
		auto& destroyQueue = EventManager::inst().getQueue<EntityDestroyEvent>();
		for (const auto& e : entities_) {
			deactivateQueue.emit({ e });
			destroyQueue.emit({ e });
			removeAllComponents(e);
		}
		for (auto& a : flags_) {
			a.reset();
		}
		entities_.clear();
		generations_.clear();
		freeIndices_.clear();
	}

	void Scene::save(const oak::string& path) {
		auto file = FileManager::inst().openFile(path, true);

		auto info = ObjInfo::make<Scene>(nullptr, "scene");

		//header:
		//version
		//entity count
		//max component count
		//text:
		//for each entity:
		//	active
		//	component count
		//	for each component:
		//		component id
		//		component data

		StreamPuper puper{ &file };

		size_t version = 0;
		size_t entityCount = entities_.size();
		size_t maxComponents = config::MAX_COMPONENTS;

		pup(puper, version, ObjInfo::make<size_t>(&info, "version"));
		pup(puper, entityCount, ObjInfo::make<size_t>(&info, "entityCount"));
		pup(puper, maxComponents, ObjInfo::make<size_t>(&info, "maxComponents"));

		size_t i = 0;
		for (auto& entity : entities_) {
			auto& filter = getComponentFilter(entity);
			auto active = isEntityActive(entity);
			auto componentCount = filter.count();
			auto entityInfo = ObjInfo::make<EntityId>(&info, oak::string{ "entity[" } + std::to_string(i).c_str() + "]");
			pup(puper, active, ObjInfo::make<size_t>(&entityInfo, "active"));
			pup(puper, componentCount, ObjInfo::make<size_t>(&entityInfo, "componentCount"));
			for (size_t j = 0; j < config::MAX_COMPONENTS; j++) {
				if (filter[j]) {
					pup(puper, j, ObjInfo::make<size_t>(&entityInfo, "componentId"));
					auto ti = ComponentTypeManager::inst().getTypeInfo(j);
					ti->serialize(puper, getComponent(entity, j), entityInfo, ti->name);
				}
			}
			i++;
		}

		FileManager::inst().closeFile(file);
	}

	void Scene::load(const oak::string& path) {
		reset();

		auto file = FileManager::inst().openFile(path);

		auto info = ObjInfo::make<Scene>(nullptr, "scene");

		StreamPuper puper{ &file };
		puper.setIo(PuperIo::IN);

		size_t version, entityCount, maxComponents;

		pup(puper, version, ObjInfo::make<size_t>(&info, "version"));
		pup(puper, entityCount, ObjInfo::make<size_t>(&info, "entityCount"));
		pup(puper, maxComponents, ObjInfo::make<size_t>(&info, "maxComponents"));

		oak_assert(maxComponents == config::MAX_COMPONENTS);

		for (size_t i = 0; i < entityCount; i++) {
			auto entityInfo = ObjInfo::make<EntityId>(&info, oak::string{ "entity[" } + std::to_string(i).c_str() + "]");
			EntityId entity = createEntity();
			bool active;
			size_t componentCount;
			pup(puper, active, ObjInfo::make<bool>(&entityInfo, "active"));
			pup(puper, componentCount, ObjInfo::make<size_t>(&entityInfo, "componentCount"));
			for (size_t j = 0; j < componentCount; j++) {
				size_t componentId;
				pup(puper, componentId, ObjInfo::make<size_t>(&entityInfo, "componentId"));
				auto comp = addComponent(entity, componentId);
				auto ti = ComponentTypeManager::inst().getTypeInfo(componentId);
				ti->serialize(puper, comp, entityInfo, ti->name);
			}
			if (active) {
				activateEntity(entity);
			}
		}
		FileManager::inst().closeFile(file);
	}

	void Scene::addComponentStorage(ComponentStorage *storage) {
		size_t tid = storage->getTypeInfo()->id;
		if (componentPools_.size() <= tid) {
			componentPools_.resize(tid + 1);
		}
		componentPools_[tid] = storage;
	}

	ComponentStorage& Scene::getComponentStorage(size_t tid) {
		return *componentPools_[tid];
	}

	const ComponentStorage& Scene::getComponentStorage(size_t tid) const {
		return *componentPools_[tid];
	}

	void Scene::ensureSize(size_t size) {
		if (componentMasks_.size() <= size) {
			componentMasks_.resize(size + 1);
		}
		if (flags_.size() <= size) {
			flags_.resize(size + 1);
		}
	}

	void Scene::removeAllComponents(EntityId entity) {
		for (size_t i = 0; i < config::MAX_COMPONENTS; i++) {
			if (componentMasks_[entity][i]) {
				removeComponent(entity, i);
			}
		}
	}

}
<commit_msg>components will be destructed before trying to add a new one<commit_after>#include "scene.h"

#include <algorithm>

#include "util/stream_puper.h"
#include "type_manager.h"
#include "event_manager.h"
#include "file_manager.h"
#include "scene_events.h"
#include "component_storage.h"

namespace oak {

	EntityId Scene::createEntity() {
		EntityId id;
		if (freeIndices_.empty()) {
			id = { static_cast<uint32_t>(generations_.size()), 0 };
			generations_.push_back(0);
		} else {
			uint32_t i = freeIndices_.front();
			id = { i, generations_[i] };
			freeIndices_.pop_front();
		}

		ensureSize(id.index);

		entities_.push_back(id);
		EventManager::inst().getQueue<EntityCreateEvent>().emit({ id });
		return id;
	}

	void Scene::destroyEntity(EntityId entity) {
		deactivateEntity(entity);
		generations_[entity]++;
		killed_.push_back(entity);
		EventManager::inst().getQueue<EntityDestroyEvent>().emit({ entity });
	}

	void Scene::activateEntity(EntityId entity) {
		flags_[entity][0] = true;
		EventManager::inst().getQueue<EntityActivateEvent>().emit({ entity });
	}

	void Scene::deactivateEntity(EntityId entity) {
		flags_[entity][0] = false;
		EventManager::inst().getQueue<EntityDeactivateEvent>().emit({ entity });
	}

	bool Scene::isEntityAlive(EntityId entity) const {
		return generations_.size() > entity.index ? generations_[entity] == entity.generation : false;
	}

	bool Scene::isEntityActive(EntityId entity) const {
		return flags_.size() > entity.index ? flags_[entity][0] : false;
	}

	void* Scene::addComponent(EntityId entity, size_t tid) {
		auto& mask = componentMasks_[entity][tid];
		auto pool = componentPools_[tid];
		if (mask) {
			pool->removeComponent(entity);
		}
		mask = true;
		return pool->addComponent(entity);
	}

	void Scene::addComponent(EntityId entity, size_t tid, const void *ptr) {
		auto& mask = componentMasks_[entity][tid];
		auto pool = componentPools_[tid];
		if (mask) {
			pool->removeComponent(entity);
		}
		mask = true;
		pool->addComponent(entity, ptr);
	}

	void Scene::removeComponent(EntityId entity, size_t tid) {
		componentMasks_[entity][tid] = false;
		componentPools_[tid]->removeComponent(entity);
	}

	void* Scene::getComponent(EntityId entity, size_t tid) {
		return componentPools_[tid]->getComponent(entity);
	}

	const void* Scene::getComponent(EntityId entity, size_t tid) const {
		return componentPools_[tid]->getComponent(entity);
	}

	bool Scene::hasComponent(EntityId entity, size_t tid) const {
		return componentMasks_[entity][tid];
	}

	const std::bitset<config::MAX_COMPONENTS>& Scene::getComponentFilter(EntityId entity) const {
		return componentMasks_[entity];
	}

	void Scene::init() {
		//create component storages for types in ComponentTypeManager
		for (auto it : ComponentTypeManager::inst().getTypes()) {
			auto ptr = static_cast<ComponentStorage*>(oak_allocator.allocate(sizeof(ComponentStorage)));
			new (ptr) ComponentStorage(it);
			ownsPools_.push_back(ptr);
			addComponentStorage(ptr);
		}
	}

	void Scene::terminate() {
		reset();
		for (auto it : ownsPools_) {
			it->~ComponentStorage();
			oak_allocator.deallocate(it, sizeof(ComponentStorage));
		}
		ownsPools_.clear();
	}

	void Scene::update() {
		for (const auto& e : killed_) {
			//remove all the entities components
			removeAllComponents(e);
			//remove the entity from the alive list and recycle the index
			entities_.erase(std::remove(std::begin(entities_), std::end(entities_), e), std::end(entities_));
			freeIndices_.push_back(e);
		}

		killed_.clear();
	}

	void Scene::reset() {
		auto& deactivateQueue = EventManager::inst().getQueue<EntityDeactivateEvent>();
		auto& destroyQueue = EventManager::inst().getQueue<EntityDestroyEvent>();
		for (const auto& e : entities_) {
			deactivateQueue.emit({ e });
			destroyQueue.emit({ e });
			removeAllComponents(e);
		}
		for (auto& a : flags_) {
			a.reset();
		}
		entities_.clear();
		generations_.clear();
		freeIndices_.clear();
	}

	void Scene::save(const oak::string& path) {
		auto file = FileManager::inst().openFile(path, true);

		auto info = ObjInfo::make<Scene>(nullptr, "scene");

		//header:
		//version
		//entity count
		//max component count
		//text:
		//for each entity:
		//	active
		//	component count
		//	for each component:
		//		component id
		//		component data

		StreamPuper puper{ &file };

		size_t version = 0;
		size_t entityCount = entities_.size();
		size_t maxComponents = config::MAX_COMPONENTS;

		pup(puper, version, ObjInfo::make<size_t>(&info, "version"));
		pup(puper, entityCount, ObjInfo::make<size_t>(&info, "entityCount"));
		pup(puper, maxComponents, ObjInfo::make<size_t>(&info, "maxComponents"));

		size_t i = 0;
		for (auto& entity : entities_) {
			auto& filter = getComponentFilter(entity);
			auto active = isEntityActive(entity);
			auto componentCount = filter.count();
			auto entityInfo = ObjInfo::make<EntityId>(&info, oak::string{ "entity[" } + std::to_string(i).c_str() + "]");
			pup(puper, active, ObjInfo::make<size_t>(&entityInfo, "active"));
			pup(puper, componentCount, ObjInfo::make<size_t>(&entityInfo, "componentCount"));
			for (size_t j = 0; j < config::MAX_COMPONENTS; j++) {
				if (filter[j]) {
					pup(puper, j, ObjInfo::make<size_t>(&entityInfo, "componentId"));
					auto ti = ComponentTypeManager::inst().getTypeInfo(j);
					ti->serialize(puper, getComponent(entity, j), entityInfo, ti->name);
				}
			}
			i++;
		}

		FileManager::inst().closeFile(file);
	}

	void Scene::load(const oak::string& path) {
		reset();

		auto file = FileManager::inst().openFile(path);

		auto info = ObjInfo::make<Scene>(nullptr, "scene");

		StreamPuper puper{ &file };
		puper.setIo(PuperIo::IN);

		size_t version, entityCount, maxComponents;

		pup(puper, version, ObjInfo::make<size_t>(&info, "version"));
		pup(puper, entityCount, ObjInfo::make<size_t>(&info, "entityCount"));
		pup(puper, maxComponents, ObjInfo::make<size_t>(&info, "maxComponents"));

		oak_assert(maxComponents == config::MAX_COMPONENTS);

		for (size_t i = 0; i < entityCount; i++) {
			auto entityInfo = ObjInfo::make<EntityId>(&info, oak::string{ "entity[" } + std::to_string(i).c_str() + "]");
			EntityId entity = createEntity();
			bool active;
			size_t componentCount;
			pup(puper, active, ObjInfo::make<bool>(&entityInfo, "active"));
			pup(puper, componentCount, ObjInfo::make<size_t>(&entityInfo, "componentCount"));
			for (size_t j = 0; j < componentCount; j++) {
				size_t componentId;
				pup(puper, componentId, ObjInfo::make<size_t>(&entityInfo, "componentId"));
				auto comp = addComponent(entity, componentId);
				auto ti = ComponentTypeManager::inst().getTypeInfo(componentId);
				ti->serialize(puper, comp, entityInfo, ti->name);
			}
			if (active) {
				activateEntity(entity);
			}
		}
		FileManager::inst().closeFile(file);
	}

	void Scene::addComponentStorage(ComponentStorage *storage) {
		size_t tid = storage->getTypeInfo()->id;
		if (componentPools_.size() <= tid) {
			componentPools_.resize(tid + 1);
		}
		componentPools_[tid] = storage;
	}

	ComponentStorage& Scene::getComponentStorage(size_t tid) {
		return *componentPools_[tid];
	}

	const ComponentStorage& Scene::getComponentStorage(size_t tid) const {
		return *componentPools_[tid];
	}

	void Scene::ensureSize(size_t size) {
		if (componentMasks_.size() <= size) {
			componentMasks_.resize(size + 1);
		}
		if (flags_.size() <= size) {
			flags_.resize(size + 1);
		}
	}

	void Scene::removeAllComponents(EntityId entity) {
		for (size_t i = 0; i < config::MAX_COMPONENTS; i++) {
			if (componentMasks_[entity][i]) {
				removeComponent(entity, i);
			}
		}
	}

}
<|endoftext|>