// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// 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 "perftest.h"

#if TEST_RAPIDJSON

#include "rapidjson/rapidjson.h"
#include "rapidjson/document.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/encodedstream.h"
#include "rapidjson/memorystream.h"

#ifdef RAPIDJSON_SSE2
#define SIMD_SUFFIX(name) name##_SSE2
#elif defined(RAPIDJSON_SSE42)
#define SIMD_SUFFIX(name) name##_SSE42
#else
#define SIMD_SUFFIX(name) name
#endif

using namespace rapidjson;

class RapidJson : public PerfTest
{
public:
  RapidJson() : temp_(), doc_() {}

  virtual void SetUp()
  { PerfTest::SetUp();

    // temp buffer for insitu parsing.
    temp_ = (char *)malloc(length_ + 1);

    // Parse as a document
    EXPECT_FALSE(doc_.Parse(json_).HasParseError());

    for (size_t i = 0; i < 7; i++)
      EXPECT_FALSE(typesDoc_[i].Parse(types_[i]).HasParseError());
  }

  virtual void TearDown()
  { PerfTest::TearDown();
    free(temp_);
  }

private:
  RapidJson(const RapidJson&);
  RapidJson& operator=(const RapidJson&);

protected:
  char *temp_;
  Document doc_;
  Document typesDoc_[7];
};

TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseInsitu_DummyHandler))
{ for (size_t i = 0; i < kTrialCount; i++)
  { memcpy(temp_, json_, length_ + 1);
    InsituStringStream s(temp_);
    BaseReaderHandler<> h;
    Reader reader;
    EXPECT_TRUE(reader.Parse<kParseInsituFlag>(s, h));
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseInsitu_DummyHandler_ValidateEncoding))
{ for (size_t i = 0; i < kTrialCount; i++)
  { memcpy(temp_, json_, length_ + 1);
    InsituStringStream s(temp_);
    BaseReaderHandler<> h;
    Reader reader;
    EXPECT_TRUE(reader.Parse<kParseInsituFlag | kParseValidateEncodingFlag>(s, h));
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler))
{ for (size_t i = 0; i < kTrialCount; i++)
  { StringStream s(json_);
    BaseReaderHandler<> h;
    Reader reader;
    EXPECT_TRUE(reader.Parse(s, h));
  }
}

#define TEST_TYPED(index, Name)\
TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_##Name)) {\
    for (size_t i = 0; i < kTrialCount * 10; i++) {\
        StringStream s(types_[index]);\
        BaseReaderHandler<> h;\
        Reader reader;\
        EXPECT_TRUE(reader.Parse(s, h));\
    }\
}\
TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseInsitu_DummyHandler_##Name)) {\
    for (size_t i = 0; i < kTrialCount * 10; i++) {\
        memcpy(temp_, types_[index], typesLength_[index] + 1);\
        InsituStringStream s(temp_);\
        BaseReaderHandler<> h;\
        Reader reader;\
        EXPECT_TRUE(reader.Parse<kParseInsituFlag>(s, h));\
    }\
}

TEST_TYPED(0, Booleans)
TEST_TYPED(1, Floats)
TEST_TYPED(2, Guids)
TEST_TYPED(3, Integers)
TEST_TYPED(4, Mixed)
TEST_TYPED(5, Nulls)
TEST_TYPED(6, Paragraphs)

#undef TEST_TYPED

TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_FullPrecision))
{ for (size_t i = 0; i < kTrialCount; i++)
  { StringStream s(json_);
    BaseReaderHandler<> h;
    Reader reader;
    EXPECT_TRUE(reader.Parse<kParseFullPrecisionFlag>(s, h));
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterative_DummyHandler))
{ for (size_t i = 0; i < kTrialCount; i++)
  { StringStream s(json_);
    BaseReaderHandler<> h;
    Reader reader;
    EXPECT_TRUE(reader.Parse<kParseIterativeFlag>(s, h));
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterativeInsitu_DummyHandler))
{ for (size_t i = 0; i < kTrialCount; i++)
  { memcpy(temp_, json_, length_ + 1);
    InsituStringStream s(temp_);
    BaseReaderHandler<> h;
    Reader reader;
    EXPECT_TRUE(reader.Parse<kParseIterativeFlag|kParseInsituFlag>(s, h));
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_ValidateEncoding))
{ for (size_t i = 0; i < kTrialCount; i++)
  { StringStream s(json_);
    BaseReaderHandler<> h;
    Reader reader;
    EXPECT_TRUE(reader.Parse<kParseValidateEncodingFlag>(s, h));
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseInsitu_MemoryPoolAllocator))
{ for (size_t i = 0; i < kTrialCount; i++)
  { memcpy(temp_, json_, length_ + 1);
    Document doc;
    doc.ParseInsitu(temp_);
    ASSERT_TRUE(doc.IsObject());
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseIterativeInsitu_MemoryPoolAllocator))
{ for (size_t i = 0; i < kTrialCount; i++)
  { memcpy(temp_, json_, length_ + 1);
    Document doc;
    doc.ParseInsitu<kParseIterativeFlag>(temp_);
    ASSERT_TRUE(doc.IsObject());
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(DocumentParse_MemoryPoolAllocator))
{ for (size_t i = 0; i < kTrialCount; i++)
  { Document doc;
    doc.Parse(json_);
    ASSERT_TRUE(doc.IsObject());
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseLength_MemoryPoolAllocator))
{ for (size_t i = 0; i < kTrialCount; i++)
  { Document doc;
    doc.Parse(json_, length_);
    ASSERT_TRUE(doc.IsObject());
  }
}

#if RAPIDJSON_HAS_STDSTRING
TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseStdString_MemoryPoolAllocator))
{ const std::string s(json_, length_);
  for (size_t i = 0; i < kTrialCount; i++)
  { Document doc;
    doc.Parse(s);
    ASSERT_TRUE(doc.IsObject());
  }
}
#endif

TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseIterative_MemoryPoolAllocator))
{ for (size_t i = 0; i < kTrialCount; i++)
  { Document doc;
    doc.Parse<kParseIterativeFlag>(json_);
    ASSERT_TRUE(doc.IsObject());
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(DocumentParse_CrtAllocator))
{ for (size_t i = 0; i < kTrialCount; i++)
  { memcpy(temp_, json_, length_ + 1);
    GenericDocument<UTF8<>, CrtAllocator> doc;
    doc.Parse(temp_);
    ASSERT_TRUE(doc.IsObject());
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseEncodedInputStream_MemoryStream))
{ for (size_t i = 0; i < kTrialCount; i++)
  { MemoryStream ms(json_, length_);
    EncodedInputStream<UTF8<>, MemoryStream> is(ms);
    Document doc;
    doc.ParseStream<0, UTF8<> >(is);
    ASSERT_TRUE(doc.IsObject());
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseAutoUTFInputStream_MemoryStream))
{ for (size_t i = 0; i < kTrialCount; i++)
  { MemoryStream ms(json_, length_);
    AutoUTFInputStream<unsigned, MemoryStream> is(ms);
    Document doc;
    doc.ParseStream<0, AutoUTF<unsigned> >(is);
    ASSERT_TRUE(doc.IsObject());
  }
}

template<typename T>
size_t Traverse(const T& value)
{ size_t count = 1;
  switch(value.GetType())
  { case kObjectType:
      for (typename T::ConstMemberIterator itr = value.MemberBegin(); itr != value.MemberEnd(); ++itr)
      { count++;    // name
        count += Traverse(itr->value);
      }
      break;

    case kArrayType:
      for (typename T::ConstValueIterator itr = value.Begin(); itr != value.End(); ++itr)
        count += Traverse(*itr);
      break;

    default:
      // Do nothing.
      break;
  }
  return count;
}

TEST_F(RapidJson, DocumentTraverse)
{ for (size_t i = 0; i < kTrialCount; i++)
  { size_t count = Traverse(doc_);
    EXPECT_EQ(4339u, count);
    //if (i == 0)
    //  std::cout << count << std::endl;
  }
}

#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif

struct ValueCounter : public BaseReaderHandler<>
{ ValueCounter() : count_(1) {}   // root

  bool EndObject(SizeType memberCount) { count_ += memberCount * 2; return true; }
  bool EndArray(SizeType elementCount) { count_ += elementCount; return true; }

  SizeType count_;
};

#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif

TEST_F(RapidJson, DocumentAccept)
{ for (size_t i = 0; i < kTrialCount; i++)
  { ValueCounter counter;
    doc_.Accept(counter);
    EXPECT_EQ(4339u, counter.count_);
  }
}

struct NullStream
{ typedef char Ch;

  NullStream() /*: length_(0)*/ {}
  void Put(Ch) { /*++length_;*/ }
  void Flush() {}
  //size_t length_;
};

TEST_F(RapidJson, Writer_NullStream)
{ for (size_t i = 0; i < kTrialCount; i++)
  { NullStream s;
    Writer<NullStream> writer(s);
    doc_.Accept(writer);
    //if (i == 0)
    //  std::cout << s.length_ << std::endl;
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(Writer_StringBuffer))
{ for (size_t i = 0; i < kTrialCount; i++)
  { StringBuffer s(0, 1024 * 1024);
    Writer<StringBuffer> writer(s);
    doc_.Accept(writer);
    const char* str = s.GetString();
    (void)str;
    //if (i == 0)
    //  std::cout << strlen(str) << std::endl;
  }
}

#define TEST_TYPED(index, Name)\
TEST_F(RapidJson, SIMD_SUFFIX(Writer_StringBuffer_##Name)) {\
    for (size_t i = 0; i < kTrialCount * 10; i++) {\
        StringBuffer s(0, 1024 * 1024);\
        Writer<StringBuffer> writer(s);\
        typesDoc_[index].Accept(writer);\
        const char* str = s.GetString();\
        (void)str;\
    }\
}

TEST_TYPED(0, Booleans)
TEST_TYPED(1, Floats)
TEST_TYPED(2, Guids)
TEST_TYPED(3, Integers)
TEST_TYPED(4, Mixed)
TEST_TYPED(5, Nulls)
TEST_TYPED(6, Paragraphs)

#undef TEST_TYPED

TEST_F(RapidJson, SIMD_SUFFIX(PrettyWriter_StringBuffer))
{ for (size_t i = 0; i < kTrialCount; i++)
  { StringBuffer s(0, 2048 * 1024);
    PrettyWriter<StringBuffer> writer(s);
    writer.SetIndent(' ', 1);
    doc_.Accept(writer);
    const char* str = s.GetString();
    (void)str;
    //if (i == 0)
    //  std::cout << strlen(str) << std::endl;
  }
}

TEST_F(RapidJson, internal_Pow10)
{ double sum = 0;
  for (size_t i = 0; i < kTrialCount * kTrialCount; i++)
    sum += internal::Pow10(int(i & 255));
  EXPECT_GT(sum, 0.0);
}

TEST_F(RapidJson, SkipWhitespace_Basic)
{ for (size_t i = 0; i < kTrialCount; i++)
  { rapidjson::StringStream s(whitespace_);
    while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t')
      s.Take();
    ASSERT_EQ('[', s.Peek());
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(SkipWhitespace))
{ for (size_t i = 0; i < kTrialCount; i++)
  { rapidjson::StringStream s(whitespace_);
    rapidjson::SkipWhitespace(s);
    ASSERT_EQ('[', s.Peek());
  }
}

TEST_F(RapidJson, SkipWhitespace_strspn)
{ for (size_t i = 0; i < kTrialCount; i++)
  { const char* s = whitespace_ + std::strspn(whitespace_, " \t\r\n");
    ASSERT_EQ('[', *s);
  }
}

TEST_F(RapidJson, UTF8_Validate)
{ NullStream os;

  for (size_t i = 0; i < kTrialCount; i++)
  { StringStream is(json_);
    bool result = true;
    while (is.Peek() != '\0')
      result &= UTF8<>::Validate(is, os);
    EXPECT_TRUE(result);
  }
}

TEST_F(RapidJson, FileReadStream)
{ for (size_t i = 0; i < kTrialCount; i++)
  { FILE *fp = fopen(filename_, "rb");
    char buffer[65536];
    FileReadStream s(fp, buffer, sizeof(buffer));
    while (s.Take() != '\0')
      ;
    fclose(fp);
  }
}

TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_FileReadStream))
{ for (size_t i = 0; i < kTrialCount; i++)
  { FILE *fp = fopen(filename_, "rb");
    char buffer[65536];
    FileReadStream s(fp, buffer, sizeof(buffer));
    BaseReaderHandler<> h;
    Reader reader;
    reader.Parse(s, h);
    fclose(fp);
  }
}

TEST_F(RapidJson, StringBuffer)
{ StringBuffer sb;
  for (int i = 0; i < 32 * 1024 * 1024; i++)
    sb.Put(i & 0x7f);
}

#endif // TEST_RAPIDJSON
