//  Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve.
//
// 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.
/*
  Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.

  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.
*/

#pragma once

#include <ostream>
#include <string>

namespace paddle {
namespace string {

// Piece points into a std::string object but doesn't own the
// string.  It is for efficient access to strings.  Like Go's string
// type.  Not that Piece doesn't mutate the underlying string,
// so it is thread-safe given that the underlying string doesn't
// change.  Because Piece contains a little data members, and
// its syntax is simple as it doesn't own/manage the string, it is
// cheap to construct Pieces and pass them around.
class Piece {
 public:
  static const size_t npos = static_cast<size_t>(-1);

  // We provide non-explicit singleton constructors so users can
  // pass in a "const char*" or a "string" wherever a "Piece"
  // is expected.  These constructors ensure that if data_ is NULL,
  // size_ is 0.
  Piece();
  Piece(const char* d, size_t n);
  Piece(const char* d);         // NOLINT: accept C string into Piece.
  Piece(const std::string& s);  // NOLINT: accept C++ string into Piece.

  const char* data() const { return data_; }
  size_t len() const { return size_; }

  char operator[](size_t n) const;

  // Piece doesn't own the string, so both iterator and const
  // iterator are const char* indeed.
  typedef const char* const_iterator;
  typedef const char* iterator;
  iterator begin() const { return data_; }
  iterator end() const { return data_ + size_; }

  // Return a string that contains the copy of the referenced data.
  std::string ToString() const { return std::string(data_, size_); }

 private:
  const char* data_;
  size_t size_;

  // Intentionally copyable
};

int Compare(Piece a, Piece b);

bool operator==(Piece x, Piece y);
bool operator!=(Piece x, Piece y);
bool operator<(Piece x, Piece y);
bool operator>(Piece x, Piece y);
bool operator<=(Piece x, Piece y);
bool operator>=(Piece x, Piece y);

bool HasPrefix(Piece s, Piece prefix);
bool HasSuffix(Piece s, Piece suffix);

Piece SkipPrefix(Piece s, size_t n);
Piece SkipSuffix(Piece s, size_t n);

// Skip the prefix (or suffix) if it matches with the string.
Piece TrimPrefix(Piece s, Piece prefix);
Piece TrimSuffix(Piece s, Piece suffix);

// Returns if s contains sub.  Any s except for empty s contains an
// empty sub.
bool Contains(Piece s, Piece sub);

// Return the first occurrence of sub in s, or npos.  If both s and
// sub is empty, it returns npos; otherwise, if only sub is empty, it
// returns 0.
size_t Index(Piece s, Piece sub);

// Return the first occurrence of c in s[pos:end], or npos.
size_t Find(Piece s, char c, size_t pos);

// Search range is [0..pos] inclusive.  If pos == npos, search everything.
size_t RFind(Piece s, char c, size_t pos);

Piece SubStr(Piece s, size_t pos, size_t n);

// allow Piece to be logged
std::ostream& operator<<(std::ostream& o, Piece piece);

}  // namespace string
}  // namespace paddle
