1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
Jip J. Dekker fad1b07018 Squashed 'software/minizinc/' content from commit 4f10c8205
git-subtree-dir: software/minizinc
git-subtree-split: 4f10c82056ffcb1041d7ffef29d77a7eef92cf76
2021-06-16 14:06:46 +10:00

268 lines
7.9 KiB
C++

/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* Main authors:
* Guido Tack <guido.tack@monash.edu>
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
#include <minizinc/gc.hh>
#include <algorithm>
#include <cstring>
#include <functional>
#include <iostream>
#include <string>
#include <unordered_map>
namespace MiniZinc {
class ASTStringData;
/**
* \brief Handle for an interned garbage collected string
*/
class ASTString {
protected:
/// String
ASTStringData* _s = nullptr;
public:
/// Default constructor
ASTString() = default;
/// Constructor
ASTString(const std::string& s);
/// Constructor
ASTString(ASTStringData* s) : _s(s){};
/// Copy constructor
ASTString(const ASTString& s) = default;
/// Assignment operator
ASTString& operator=(const ASTString& s) = default;
/// Size of the string
size_t size() const;
/// Underlying C string object
const char* c_str() const; // NOLINT(readability-identifier-naming)
/// Underlying string implementation
ASTStringData* aststr() const { return _s; }
/// Return if string is equal to \a s
bool operator==(const ASTString& s) const;
/// Return if string is not equal to \a s
bool operator!=(const ASTString& s) const;
/// Return if string is less than \a s
bool operator<(const ASTString& s) const;
/// Return if string is equal to \a s
bool operator==(const std::string& s) const;
/// Return if string is not equal to \a s
bool operator!=(const std::string& s) const;
/// Return if string ends with \a s
bool endsWith(const std::string& s) const;
/// Return if string begins with \a s
bool beginsWith(const std::string& s) const;
/// Returns a substring [pos, pos+count).
std::string substr(size_t pos = 0, size_t count = std::string::npos) const;
// Finds the last character equal to one of characters in the given character sequence.
size_t findLastOf(char ch, size_t pos = std::string::npos) const noexcept;
// Finds the first character equal to the given character sequence.
size_t find(char ch, size_t pos = 0) const noexcept;
/// Return Levenshtein distance to \a s
int levenshteinDistance(const ASTString& other) const;
/// Compute hash value of string
size_t hash() const;
/// Mark string during garbage collection
void mark() const;
};
/**
* \brief Print String \a s
*/
template <class Char, class Traits>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os,
const ASTString& s) {
return s.size() == 0 ? os : (os << s.c_str());
}
} // namespace MiniZinc
namespace std {
template <>
struct hash<MiniZinc::ASTString> {
public:
size_t operator()(const MiniZinc::ASTString& s) const;
};
template <>
struct equal_to<MiniZinc::ASTString> {
public:
bool operator()(const MiniZinc::ASTString& s0, const MiniZinc::ASTString& s1) const;
};
template <>
struct less<MiniZinc::ASTString> {
public:
bool operator()(const MiniZinc::ASTString& s0, const MiniZinc::ASTString& s1) const;
};
} // namespace std
namespace MiniZinc {
struct CStringHash {
public:
// FIXME: This is not an amazing hash function
size_t operator()(const std::pair<const char*, size_t>& s) const {
size_t result = 0;
const size_t prime = 31;
for (size_t i = 0; i < s.second; ++i) {
result = s.first[i] + (result * prime);
}
return result;
}
};
struct CStringEquals {
public:
bool operator()(const std::pair<const char*, size_t>& s0,
const std::pair<const char*, size_t>& s1) const {
return s0.second == s1.second && (strncmp(s0.first, s1.first, s0.second) == 0);
}
};
/**
* \brief Garbage collected interned string
*/
class ASTStringData : public ASTChunk {
friend class GC::Heap;
protected:
/// Interning Hash Map
using Interner = std::unordered_map<std::pair<const char*, size_t>, ASTStringData*, CStringHash,
CStringEquals>;
static Interner& interner();
/// Constructor
ASTStringData(const std::string& s);
public:
/// Allocate and initialise as \a s
static ASTStringData* a(const std::string& s);
/// Return underlying C-style string
// NOLINTNEXTLINE(readability-identifier-naming)
const char* c_str() const { return _data + sizeof(size_t); }
/// Return size of string
size_t size() const {
return static_cast<unsigned int>(_size) - static_cast<unsigned int>(sizeof(size_t)) - 1;
}
/// Access character at position \a i
char operator[](unsigned int i) {
assert(i < size());
return _data[sizeof(size_t) + i];
}
/// Return hash value of string
size_t hash() const { return reinterpret_cast<const size_t*>(_data)[0]; }
/// Mark for garbage collection
void mark() const { _gcMark = 1; }
protected:
/// GC Destructor
void destroy() const {
assert(interner().find({this->c_str(), this->size()}) != interner().end());
interner().erase({this->c_str(), this->size()});
};
};
inline ASTString::ASTString(const std::string& s) : _s(ASTStringData::a(s)) {}
inline size_t ASTString::size() const { return _s != nullptr ? _s->size() : 0; }
// NOLINTNEXTLINE(readability-identifier-naming)
inline const char* ASTString::c_str() const { return _s != nullptr ? _s->c_str() : nullptr; }
inline void ASTString::mark() const {
if (_s != nullptr) {
_s->mark();
}
}
inline bool ASTString::operator==(const ASTString& s) const { return _s == s._s; }
inline bool ASTString::operator!=(const ASTString& s) const { return _s != s._s; }
inline bool ASTString::operator<(const ASTString& s) const {
if (size() == 0) {
return 0 < s.size();
}
unsigned int size = std::min(_s->size(), s.size());
int cmp = strncmp(_s->c_str(), s.c_str(), size);
if (cmp == 0) {
return _s->size() < s.size();
}
return cmp < 0;
}
inline bool ASTString::operator==(const std::string& s) const {
return size() == s.size() && (size() == 0 || strncmp(_s->c_str(), s.c_str(), size()) == 0);
}
inline bool ASTString::operator!=(const std::string& s) const { return !(*this == s); }
inline bool ASTString::endsWith(const std::string& s) const {
return size() >= s.size() &&
(size() == 0 || strncmp(_s->c_str() + size() - s.size(), s.c_str(), s.size()) == 0);
}
inline bool ASTString::beginsWith(const std::string& s) const {
return size() >= s.size() && (size() == 0 || strncmp(_s->c_str(), s.c_str(), s.size()) == 0);
}
inline std::string ASTString::substr(size_t pos, size_t count) const {
if (pos > size()) {
throw std::out_of_range("ASTString::substr pos out of range");
}
if (count == std::string::npos) {
return std::string(c_str() + pos, size() - pos);
}
return std::string(c_str() + pos, std::min(size() - pos, count));
}
inline size_t ASTString::findLastOf(char ch, size_t pos) const noexcept {
const char* str = c_str();
for (int i = std::min(size() - 1, pos); i >= 0; --i) {
if (str[i] == ch) {
return i;
}
}
return std::string::npos;
}
inline size_t ASTString::find(char ch, size_t pos) const noexcept {
if (pos >= size()) {
return std::string::npos;
}
const char* str = c_str();
for (int i = pos; i < size(); ++i) {
if (str[i] == ch) {
return i;
}
}
return std::string::npos;
}
inline size_t ASTString::hash() const { return _s != nullptr ? _s->hash() : 0; }
} // namespace MiniZinc
namespace std {
inline size_t hash<MiniZinc::ASTString>::operator()(const MiniZinc::ASTString& s) const {
return s.hash();
}
inline bool equal_to<MiniZinc::ASTString>::operator()(const MiniZinc::ASTString& s0,
const MiniZinc::ASTString& s1) const {
return s0 == s1;
}
inline bool less<MiniZinc::ASTString>::operator()(const MiniZinc::ASTString& s0,
const MiniZinc::ASTString& s1) const {
return s0 < s1;
}
} // namespace std