added support for windows
This commit is contained in:
parent
3f58464c6c
commit
4affce9f0f
|
|
@ -388,3 +388,7 @@ Makefile
|
|||
.idea/
|
||||
|
||||
cmake-build*/
|
||||
|
||||
build/
|
||||
|
||||
bin/
|
||||
|
|
@ -1,23 +1,12 @@
|
|||
cmake_minimum_required(VERSION 4.1)
|
||||
project(basic-vcs)
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(ZStrGitRepo
|
||||
GIT_REPOSITORY "https://github.com/mateidavid/zstr" # can also be a local filesystem path!
|
||||
GIT_TAG "master"
|
||||
)
|
||||
FetchContent_MakeAvailable(ZStrGitRepo) # defines INTERFACE target 'zstr::zstr'
|
||||
|
||||
set(BUILD_TZ_LIB ON)# timezone support
|
||||
|
||||
FetchContent_Declare( date_src
|
||||
GIT_REPOSITORY https://gitlab.com/JosephA1997/date.git
|
||||
GIT_TAG "master"
|
||||
)
|
||||
FetchContent_MakeAvailable(date_src)
|
||||
add_definitions(-DMINIZ=1)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
|
||||
|
||||
add_executable(basic-vcs src/main.cpp
|
||||
include/sha1.hpp
|
||||
include/init.h
|
||||
|
|
@ -33,7 +22,17 @@ add_executable(basic-vcs src/main.cpp
|
|||
include/hash-object.h
|
||||
src/commands/commit-tree.cpp
|
||||
include/commit-tree.h
|
||||
include/commit-tree.h)
|
||||
include/commit-tree.h
|
||||
src/miniz.c
|
||||
include/miniz.h
|
||||
src/tz.cpp)
|
||||
|
||||
target_link_libraries(basic-vcs PRIVATE zstr::zstr date::date date::date-tz)
|
||||
target_link_libraries(basic-vcs PRIVATE )
|
||||
target_include_directories(basic-vcs PRIVATE include)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT basic-vcs)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
|
||||
endif()
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
mkdir build
|
||||
cd build
|
||||
cmake -S .. -B .
|
||||
PAUSE
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "cat-file.h"
|
||||
#include "clone.h"
|
||||
#include "hash-object.h"
|
||||
#include "init.h"
|
||||
#include "ls-tree.h"
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,237 @@
|
|||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* This namespace defines wrappers for std::ifstream, std::ofstream, and
|
||||
* std::fstream objects. The wrappers perform the following steps:
|
||||
* - check the open modes make sense
|
||||
* - check that the call to open() is successful
|
||||
* - (for input streams) check that the opened file is peek-able
|
||||
* - turn on the badbit in the exception mask
|
||||
*/
|
||||
namespace strict_fstream
|
||||
{
|
||||
|
||||
// Help people out a bit, it seems like this is a common recommenation since
|
||||
// musl breaks all over the place.
|
||||
#if defined(__NEED_size_t) && !defined(__MUSL__)
|
||||
#warning "It seems to be recommended to patch in a define for __MUSL__ if you use musl globally: https://www.openwall.com/lists/musl/2013/02/10/5"
|
||||
#define __MUSL__
|
||||
#endif
|
||||
|
||||
// Workaround for broken musl implementation
|
||||
// Since musl insists that they are perfectly compatible, ironically enough,
|
||||
// they don't officially have a __musl__ or similar. But __NEED_size_t is defined in their
|
||||
// relevant header (and not in working implementations), so we can use that.
|
||||
#ifdef __MUSL__
|
||||
#warning "Working around broken strerror_r() implementation in musl, remove when musl is fixed"
|
||||
#endif
|
||||
|
||||
// Non-gnu variants of strerror_* don't necessarily null-terminate if
|
||||
// truncating, so we have to do things manually.
|
||||
inline std::string trim_to_null(const std::vector<char> &buff)
|
||||
{
|
||||
std::string ret(buff.begin(), buff.end());
|
||||
|
||||
const std::string::size_type pos = ret.find('\0');
|
||||
if (pos == std::string::npos) {
|
||||
ret += " [...]"; // it has been truncated
|
||||
} else {
|
||||
ret.resize(pos);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Overload of error-reporting function, to enable use with VS and non-GNU
|
||||
/// POSIX libc's
|
||||
/// Ref:
|
||||
/// - http://stackoverflow.com/a/901316/717706
|
||||
static std::string strerror()
|
||||
{
|
||||
// Can't use std::string since we're pre-C++17
|
||||
std::vector<char> buff(256, '\0');
|
||||
|
||||
#ifdef _WIN32
|
||||
// Since strerror_s might set errno itself, we need to store it.
|
||||
const int err_num = errno;
|
||||
if (strerror_s(buff.data(), buff.size(), err_num) != 0) {
|
||||
return trim_to_null(buff);
|
||||
} else {
|
||||
return "Unknown error (" + std::to_string(err_num) + ")";
|
||||
}
|
||||
#elif ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 || defined(__APPLE__) || defined(__FreeBSD__)) && ! _GNU_SOURCE) || defined(__MUSL__) || defined(__ANDROID_API__)
|
||||
// XSI-compliant strerror_r()
|
||||
const int err_num = errno; // See above
|
||||
if (strerror_r(err_num, buff.data(), buff.size()) == 0) {
|
||||
return trim_to_null(buff);
|
||||
} else {
|
||||
return "Unknown error (" + std::to_string(err_num) + ")";
|
||||
}
|
||||
#else
|
||||
// GNU-specific strerror_r()
|
||||
char * p = strerror_r(errno, &buff[0], buff.size());
|
||||
return std::string(p, std::strlen(p));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Exception class thrown by failed operations.
|
||||
class Exception
|
||||
: public std::exception
|
||||
{
|
||||
public:
|
||||
Exception(const std::string& msg) : _msg(msg) {}
|
||||
const char * what() const noexcept { return _msg.c_str(); }
|
||||
private:
|
||||
std::string _msg;
|
||||
}; // class Exception
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
struct static_method_holder
|
||||
{
|
||||
static std::string mode_to_string(std::ios_base::openmode mode)
|
||||
{
|
||||
static const int n_modes = 6;
|
||||
static const std::ios_base::openmode mode_val_v[n_modes] =
|
||||
{
|
||||
std::ios_base::in,
|
||||
std::ios_base::out,
|
||||
std::ios_base::app,
|
||||
std::ios_base::ate,
|
||||
std::ios_base::trunc,
|
||||
std::ios_base::binary
|
||||
};
|
||||
|
||||
static const char * mode_name_v[n_modes] =
|
||||
{
|
||||
"in",
|
||||
"out",
|
||||
"app",
|
||||
"ate",
|
||||
"trunc",
|
||||
"binary"
|
||||
};
|
||||
std::string res;
|
||||
for (int i = 0; i < n_modes; ++i)
|
||||
{
|
||||
if (mode & mode_val_v[i])
|
||||
{
|
||||
res += (! res.empty()? "|" : "");
|
||||
res += mode_name_v[i];
|
||||
}
|
||||
}
|
||||
if (res.empty()) res = "none";
|
||||
return res;
|
||||
}
|
||||
static void check_mode(const std::string& filename, std::ios_base::openmode mode)
|
||||
{
|
||||
if ((mode & std::ios_base::trunc) && ! (mode & std::ios_base::out))
|
||||
{
|
||||
throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: trunc and not out");
|
||||
}
|
||||
else if ((mode & std::ios_base::app) && ! (mode & std::ios_base::out))
|
||||
{
|
||||
throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: app and not out");
|
||||
}
|
||||
else if ((mode & std::ios_base::trunc) && (mode & std::ios_base::app))
|
||||
{
|
||||
throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: trunc and app");
|
||||
}
|
||||
}
|
||||
static void check_open(std::ios * s_p, const std::string& filename, std::ios_base::openmode mode)
|
||||
{
|
||||
if (s_p->fail())
|
||||
{
|
||||
throw Exception(std::string("strict_fstream: open('")
|
||||
+ filename + "'," + mode_to_string(mode) + "): open failed: "
|
||||
+ strerror());
|
||||
}
|
||||
}
|
||||
static void check_peek(std::istream * is_p, const std::string& filename, std::ios_base::openmode mode)
|
||||
{
|
||||
bool peek_failed = true;
|
||||
try
|
||||
{
|
||||
is_p->peek();
|
||||
peek_failed = is_p->fail();
|
||||
}
|
||||
catch (const std::ios_base::failure &) {}
|
||||
if (peek_failed)
|
||||
{
|
||||
throw Exception(std::string("strict_fstream: open('")
|
||||
+ filename + "'," + mode_to_string(mode) + "): peek failed: "
|
||||
+ strerror());
|
||||
}
|
||||
is_p->clear();
|
||||
}
|
||||
}; // struct static_method_holder
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class ifstream
|
||||
: public std::ifstream
|
||||
{
|
||||
public:
|
||||
ifstream() = default;
|
||||
ifstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
|
||||
{
|
||||
open(filename, mode);
|
||||
}
|
||||
void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
|
||||
{
|
||||
mode |= std::ios_base::in;
|
||||
exceptions(std::ios_base::badbit);
|
||||
detail::static_method_holder::check_mode(filename, mode);
|
||||
std::ifstream::open(filename, mode);
|
||||
detail::static_method_holder::check_open(this, filename, mode);
|
||||
detail::static_method_holder::check_peek(this, filename, mode);
|
||||
}
|
||||
}; // class ifstream
|
||||
|
||||
class ofstream
|
||||
: public std::ofstream
|
||||
{
|
||||
public:
|
||||
ofstream() = default;
|
||||
ofstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out)
|
||||
{
|
||||
open(filename, mode);
|
||||
}
|
||||
void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out)
|
||||
{
|
||||
mode |= std::ios_base::out;
|
||||
exceptions(std::ios_base::badbit);
|
||||
detail::static_method_holder::check_mode(filename, mode);
|
||||
std::ofstream::open(filename, mode);
|
||||
detail::static_method_holder::check_open(this, filename, mode);
|
||||
}
|
||||
}; // class ofstream
|
||||
|
||||
class fstream
|
||||
: public std::fstream
|
||||
{
|
||||
public:
|
||||
fstream() = default;
|
||||
fstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
|
||||
{
|
||||
open(filename, mode);
|
||||
}
|
||||
void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
|
||||
{
|
||||
if (! (mode & std::ios_base::out)) mode |= std::ios_base::in;
|
||||
exceptions(std::ios_base::badbit);
|
||||
detail::static_method_holder::check_mode(filename, mode);
|
||||
std::fstream::open(filename, mode);
|
||||
detail::static_method_holder::check_open(this, filename, mode);
|
||||
detail::static_method_holder::check_peek(this, filename, mode);
|
||||
}
|
||||
}; // class fstream
|
||||
|
||||
} // namespace strict_fstream
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,315 @@
|
|||
#ifndef TZ_PRIVATE_H
|
||||
#define TZ_PRIVATE_H
|
||||
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2015, 2016 Howard Hinnant
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// Our apologies. When the previous paragraph was written, lowercase had not yet
|
||||
// been invented (that would involve another several millennia of evolution).
|
||||
// We did not mean to shout.
|
||||
|
||||
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
||||
#include "tz.h"
|
||||
#else
|
||||
#include "date.h"
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
namespace date
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
#if !USE_OS_TZDB
|
||||
|
||||
enum class tz {utc, local, standard};
|
||||
|
||||
//forward declare to avoid warnings in gcc 6.2
|
||||
class MonthDayTime;
|
||||
std::istream& operator>>(std::istream& is, MonthDayTime& x);
|
||||
std::ostream& operator<<(std::ostream& os, const MonthDayTime& x);
|
||||
|
||||
|
||||
class MonthDayTime
|
||||
{
|
||||
private:
|
||||
struct pair
|
||||
{
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
pair() : month_day_(date::jan / 1), weekday_(0U) {}
|
||||
|
||||
pair(const date::month_day& month_day, const date::weekday& weekday)
|
||||
: month_day_(month_day), weekday_(weekday) {}
|
||||
#endif
|
||||
|
||||
date::month_day month_day_;
|
||||
date::weekday weekday_;
|
||||
};
|
||||
|
||||
enum Type {month_day, month_last_dow, lteq, gteq};
|
||||
|
||||
Type type_{month_day};
|
||||
|
||||
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
||||
union U
|
||||
#else
|
||||
struct U
|
||||
#endif
|
||||
{
|
||||
date::month_day month_day_;
|
||||
date::month_weekday_last month_weekday_last_;
|
||||
pair month_day_weekday_;
|
||||
|
||||
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
||||
U() : month_day_{date::jan/1} {}
|
||||
#else
|
||||
U() :
|
||||
month_day_(date::jan/1),
|
||||
month_weekday_last_(date::month(0U), date::weekday_last(date::weekday(0U)))
|
||||
{}
|
||||
|
||||
#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
||||
|
||||
U& operator=(const date::month_day& x);
|
||||
U& operator=(const date::month_weekday_last& x);
|
||||
U& operator=(const pair& x);
|
||||
} u;
|
||||
|
||||
std::chrono::hours h_{0};
|
||||
std::chrono::minutes m_{0};
|
||||
std::chrono::seconds s_{0};
|
||||
tz zone_{tz::local};
|
||||
|
||||
public:
|
||||
MonthDayTime() = default;
|
||||
MonthDayTime(local_seconds tp, tz timezone);
|
||||
MonthDayTime(const date::month_day& md, tz timezone);
|
||||
|
||||
date::day day() const;
|
||||
date::month month() const;
|
||||
tz zone() const {return zone_;}
|
||||
|
||||
void canonicalize(date::year y);
|
||||
|
||||
sys_seconds
|
||||
to_sys(date::year y, std::chrono::seconds offset, std::chrono::seconds save) const;
|
||||
sys_days to_sys_days(date::year y) const;
|
||||
|
||||
sys_seconds to_time_point(date::year y) const;
|
||||
int compare(date::year y, const MonthDayTime& x, date::year yx,
|
||||
std::chrono::seconds offset, std::chrono::minutes prev_save) const;
|
||||
|
||||
friend std::istream& operator>>(std::istream& is, MonthDayTime& x);
|
||||
friend std::ostream& operator<<(std::ostream& os, const MonthDayTime& x);
|
||||
};
|
||||
|
||||
// A Rule specifies one or more set of datetimes without using an offset.
|
||||
// Multiple dates are specified with multiple years. The years in effect
|
||||
// go from starting_year_ to ending_year_, inclusive. starting_year_ <=
|
||||
// ending_year_. save_ is in effect for times from the specified time
|
||||
// onward, including the specified time. When the specified time is
|
||||
// local, it uses the save_ from the chronologically previous Rule, or if
|
||||
// there is none, 0.
|
||||
|
||||
//forward declare to avoid warnings in gcc 6.2
|
||||
class Rule;
|
||||
bool operator==(const Rule& x, const Rule& y);
|
||||
bool operator<(const Rule& x, const Rule& y);
|
||||
bool operator==(const Rule& x, const date::year& y);
|
||||
bool operator<(const Rule& x, const date::year& y);
|
||||
bool operator==(const date::year& x, const Rule& y);
|
||||
bool operator<(const date::year& x, const Rule& y);
|
||||
bool operator==(const Rule& x, const std::string& y);
|
||||
bool operator<(const Rule& x, const std::string& y);
|
||||
bool operator==(const std::string& x, const Rule& y);
|
||||
bool operator<(const std::string& x, const Rule& y);
|
||||
std::ostream& operator<<(std::ostream& os, const Rule& r);
|
||||
|
||||
class Rule
|
||||
{
|
||||
private:
|
||||
std::string name_;
|
||||
date::year starting_year_{0};
|
||||
date::year ending_year_{0};
|
||||
MonthDayTime starting_at_;
|
||||
std::chrono::minutes save_{0};
|
||||
std::string abbrev_;
|
||||
|
||||
public:
|
||||
Rule() = default;
|
||||
explicit Rule(const std::string& s);
|
||||
Rule(const Rule& r, date::year starting_year, date::year ending_year);
|
||||
|
||||
const std::string& name() const {return name_;}
|
||||
const std::string& abbrev() const {return abbrev_;}
|
||||
|
||||
const MonthDayTime& mdt() const {return starting_at_;}
|
||||
const date::year& starting_year() const {return starting_year_;}
|
||||
const date::year& ending_year() const {return ending_year_;}
|
||||
const std::chrono::minutes& save() const {return save_;}
|
||||
|
||||
static void split_overlaps(std::vector<Rule>& rules);
|
||||
|
||||
friend bool operator==(const Rule& x, const Rule& y);
|
||||
friend bool operator<(const Rule& x, const Rule& y);
|
||||
friend bool operator==(const Rule& x, const date::year& y);
|
||||
friend bool operator<(const Rule& x, const date::year& y);
|
||||
friend bool operator==(const date::year& x, const Rule& y);
|
||||
friend bool operator<(const date::year& x, const Rule& y);
|
||||
friend bool operator==(const Rule& x, const std::string& y);
|
||||
friend bool operator<(const Rule& x, const std::string& y);
|
||||
friend bool operator==(const std::string& x, const Rule& y);
|
||||
friend bool operator<(const std::string& x, const Rule& y);
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Rule& r);
|
||||
|
||||
private:
|
||||
date::day day() const;
|
||||
date::month month() const;
|
||||
static void split_overlaps(std::vector<Rule>& rules, std::size_t i, std::size_t& e);
|
||||
static bool overlaps(const Rule& x, const Rule& y);
|
||||
static void split(std::vector<Rule>& rules, std::size_t i, std::size_t k,
|
||||
std::size_t& e);
|
||||
};
|
||||
|
||||
inline bool operator!=(const Rule& x, const Rule& y) {return !(x == y);}
|
||||
inline bool operator> (const Rule& x, const Rule& y) {return y < x;}
|
||||
inline bool operator<=(const Rule& x, const Rule& y) {return !(y < x);}
|
||||
inline bool operator>=(const Rule& x, const Rule& y) {return !(x < y);}
|
||||
|
||||
inline bool operator!=(const Rule& x, const date::year& y) {return !(x == y);}
|
||||
inline bool operator> (const Rule& x, const date::year& y) {return y < x;}
|
||||
inline bool operator<=(const Rule& x, const date::year& y) {return !(y < x);}
|
||||
inline bool operator>=(const Rule& x, const date::year& y) {return !(x < y);}
|
||||
|
||||
inline bool operator!=(const date::year& x, const Rule& y) {return !(x == y);}
|
||||
inline bool operator> (const date::year& x, const Rule& y) {return y < x;}
|
||||
inline bool operator<=(const date::year& x, const Rule& y) {return !(y < x);}
|
||||
inline bool operator>=(const date::year& x, const Rule& y) {return !(x < y);}
|
||||
|
||||
inline bool operator!=(const Rule& x, const std::string& y) {return !(x == y);}
|
||||
inline bool operator> (const Rule& x, const std::string& y) {return y < x;}
|
||||
inline bool operator<=(const Rule& x, const std::string& y) {return !(y < x);}
|
||||
inline bool operator>=(const Rule& x, const std::string& y) {return !(x < y);}
|
||||
|
||||
inline bool operator!=(const std::string& x, const Rule& y) {return !(x == y);}
|
||||
inline bool operator> (const std::string& x, const Rule& y) {return y < x;}
|
||||
inline bool operator<=(const std::string& x, const Rule& y) {return !(y < x);}
|
||||
inline bool operator>=(const std::string& x, const Rule& y) {return !(x < y);}
|
||||
|
||||
struct zonelet
|
||||
{
|
||||
enum tag {has_rule, has_save, is_empty};
|
||||
|
||||
std::chrono::seconds gmtoff_;
|
||||
tag tag_ = has_rule;
|
||||
|
||||
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
||||
union U
|
||||
#else
|
||||
struct U
|
||||
#endif
|
||||
{
|
||||
std::string rule_;
|
||||
std::chrono::minutes save_;
|
||||
|
||||
~U() {}
|
||||
U() {}
|
||||
U(const U&) {}
|
||||
U& operator=(const U&) = delete;
|
||||
} u;
|
||||
|
||||
std::string format_;
|
||||
date::year until_year_{0};
|
||||
MonthDayTime until_date_;
|
||||
sys_seconds until_utc_;
|
||||
local_seconds until_std_;
|
||||
local_seconds until_loc_;
|
||||
std::chrono::minutes initial_save_{0};
|
||||
std::string initial_abbrev_;
|
||||
std::pair<const Rule*, date::year> first_rule_{nullptr, date::year::min()};
|
||||
std::pair<const Rule*, date::year> last_rule_{nullptr, date::year::max()};
|
||||
|
||||
~zonelet();
|
||||
zonelet();
|
||||
zonelet(const zonelet& i);
|
||||
zonelet& operator=(const zonelet&) = delete;
|
||||
};
|
||||
|
||||
#else // USE_OS_TZDB
|
||||
|
||||
struct ttinfo
|
||||
{
|
||||
std::int32_t tt_gmtoff;
|
||||
unsigned char tt_isdst;
|
||||
unsigned char tt_abbrind;
|
||||
unsigned char pad[2];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ttinfo) == 8, "");
|
||||
|
||||
struct expanded_ttinfo
|
||||
{
|
||||
std::chrono::seconds offset;
|
||||
std::string abbrev;
|
||||
bool is_dst;
|
||||
};
|
||||
|
||||
struct transition
|
||||
{
|
||||
sys_seconds timepoint;
|
||||
const expanded_ttinfo* info;
|
||||
|
||||
transition(sys_seconds tp, const expanded_ttinfo* i = nullptr)
|
||||
: timepoint(tp)
|
||||
, info(i)
|
||||
{}
|
||||
|
||||
friend
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const transition& t)
|
||||
{
|
||||
date::operator<<(os, t.timepoint) << "Z ";
|
||||
if (t.info->offset >= std::chrono::seconds{0})
|
||||
os << '+';
|
||||
os << make_time(t.info->offset);
|
||||
if (t.info->is_dst > 0)
|
||||
os << " daylight ";
|
||||
else
|
||||
os << " standard ";
|
||||
os << t.info->abbrev;
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // USE_OS_TZDB
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace date
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
#include "tz.h"
|
||||
#endif
|
||||
|
||||
#endif // TZ_PRIVATE_H
|
||||
|
|
@ -0,0 +1,673 @@
|
|||
//---------------------------------------------------------
|
||||
// Copyright 2015 Ontario Institute for Cancer Research
|
||||
// Written by Matei David (matei@cs.toronto.edu)
|
||||
//---------------------------------------------------------
|
||||
|
||||
// Reference:
|
||||
// http://stackoverflow.com/questions/14086417/how-to-write-custom-input-stream-in-c
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#ifdef MINIZ
|
||||
#include <miniz.h>
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include "strict_fstream.hpp"
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#if (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__>0)
|
||||
#define CAN_MOVE_IOSTREAM
|
||||
#endif
|
||||
#else
|
||||
#define CAN_MOVE_IOSTREAM
|
||||
#endif
|
||||
|
||||
namespace zstr
|
||||
{
|
||||
|
||||
static const std::size_t default_buff_size = static_cast<std::size_t>(1 << 20);
|
||||
|
||||
/// Exception class thrown by failed zlib operations.
|
||||
class Exception
|
||||
: public std::ios_base::failure
|
||||
{
|
||||
public:
|
||||
static std::string error_to_message(z_stream * zstrm_p, int ret)
|
||||
{
|
||||
std::string msg = "zlib: ";
|
||||
switch (ret)
|
||||
{
|
||||
case Z_STREAM_ERROR:
|
||||
msg += "Z_STREAM_ERROR: ";
|
||||
break;
|
||||
case Z_DATA_ERROR:
|
||||
msg += "Z_DATA_ERROR: ";
|
||||
break;
|
||||
case Z_MEM_ERROR:
|
||||
msg += "Z_MEM_ERROR: ";
|
||||
break;
|
||||
case Z_VERSION_ERROR:
|
||||
msg += "Z_VERSION_ERROR: ";
|
||||
break;
|
||||
case Z_BUF_ERROR:
|
||||
msg += "Z_BUF_ERROR: ";
|
||||
break;
|
||||
default:
|
||||
std::ostringstream oss;
|
||||
oss << ret;
|
||||
msg += "[" + oss.str() + "]: ";
|
||||
break;
|
||||
}
|
||||
|
||||
// Prefer library-provided error string (safe). Fall back to zstrm_p->msg only if that seems safe.
|
||||
const char* lib_err = nullptr;
|
||||
#ifdef MINIZ
|
||||
lib_err = zError(ret); // mapped to mz_error
|
||||
#else
|
||||
lib_err = zError(ret);
|
||||
#endif
|
||||
if (lib_err && lib_err[0] != '\0') {
|
||||
msg += lib_err;
|
||||
} else if (zstrm_p && zstrm_p->msg) {
|
||||
// last-resort: append msg pointer. This may still be unsafe if the library left garbage,
|
||||
// but prefer library error strings which are reliable.
|
||||
msg += zstrm_p->msg;
|
||||
}
|
||||
|
||||
msg += " ("
|
||||
"next_in: " +
|
||||
std::to_string(uintptr_t(zstrm_p ? zstrm_p->next_in : nullptr)) +
|
||||
", avail_in: " +
|
||||
std::to_string(uintptr_t(zstrm_p ? zstrm_p->avail_in : 0)) +
|
||||
", next_out: " +
|
||||
std::to_string(uintptr_t(zstrm_p ? zstrm_p->next_out : nullptr)) +
|
||||
", avail_out: " +
|
||||
std::to_string(uintptr_t(zstrm_p ? zstrm_p->avail_out : 0)) +
|
||||
")";
|
||||
return msg;
|
||||
}
|
||||
|
||||
Exception(z_stream * zstrm_p, int ret)
|
||||
: std::ios_base::failure(error_to_message(zstrm_p, ret))
|
||||
{
|
||||
}
|
||||
}; // class Exception
|
||||
|
||||
namespace detail
|
||||
{
|
||||
namespace WindowBits {
|
||||
/*
|
||||
* Definitions for the valid values of the windowBits/windowSize parameter
|
||||
*
|
||||
* Based on the zlib manual (https://zlib.net/manual.html)
|
||||
* See sections DeflateInit2 and InflateInit2
|
||||
*/
|
||||
|
||||
namespace Raw {
|
||||
constexpr int RAW = -8;
|
||||
constexpr int RAW1 = -9;
|
||||
constexpr int RAW2 = -10;
|
||||
constexpr int RAW3 = -11;
|
||||
constexpr int RAW4 = -12;
|
||||
constexpr int RAW5 = -13;
|
||||
constexpr int RAW6 = -14;
|
||||
constexpr int RAW7 = -15;
|
||||
} // namespace RAW
|
||||
constexpr int RAW = Raw::RAW;
|
||||
|
||||
namespace ZLIB {
|
||||
constexpr int AUTO = 0;
|
||||
constexpr int _256B = 8;
|
||||
constexpr int MIN = _256B;
|
||||
constexpr int _512B = 9;
|
||||
constexpr int _1KiB = 10;
|
||||
constexpr int _2KiB = 11;
|
||||
constexpr int _4KiB = 12;
|
||||
constexpr int _8KiB = 13;
|
||||
constexpr int _16KiB = 14;
|
||||
constexpr int _32KiB = 15;
|
||||
constexpr int MAX = _32KiB;
|
||||
} // namespace ZLIB
|
||||
|
||||
// For use with older version: De-/InflateInit()
|
||||
constexpr int DEFAULT_DEFLATE = ZLIB::MAX;
|
||||
constexpr int DEFAULT_INFLATE = ZLIB::MAX;
|
||||
// For use with current version: De-/InflateInit2()
|
||||
constexpr int DEFAULT_DEFLATE2 = ZLIB::MIN;
|
||||
constexpr int DEFAULT_INFLATE2 = ZLIB::MAX;
|
||||
|
||||
constexpr int ADD_TO_ENABLE_GZIP_AND_DISABLE_ZLIB = 16;
|
||||
|
||||
namespace GZIP {
|
||||
constexpr int AUTO = ZLIB::AUTO + ADD_TO_ENABLE_GZIP_AND_DISABLE_ZLIB;
|
||||
constexpr int _256B = ZLIB::_256B + ADD_TO_ENABLE_GZIP_AND_DISABLE_ZLIB;
|
||||
constexpr int MIN = _256B;
|
||||
constexpr int _512B = ZLIB::_512B + ADD_TO_ENABLE_GZIP_AND_DISABLE_ZLIB;
|
||||
constexpr int _1KiB = ZLIB::_1KiB + ADD_TO_ENABLE_GZIP_AND_DISABLE_ZLIB;
|
||||
constexpr int _2KiB = ZLIB::_2KiB + ADD_TO_ENABLE_GZIP_AND_DISABLE_ZLIB;
|
||||
constexpr int _4KiB = ZLIB::_4KiB + ADD_TO_ENABLE_GZIP_AND_DISABLE_ZLIB;
|
||||
constexpr int _8KiB = ZLIB::_8KiB + ADD_TO_ENABLE_GZIP_AND_DISABLE_ZLIB;
|
||||
constexpr int _16KiB = ZLIB::_16KiB + ADD_TO_ENABLE_GZIP_AND_DISABLE_ZLIB;
|
||||
constexpr int _32KiB = ZLIB::_32KiB + ADD_TO_ENABLE_GZIP_AND_DISABLE_ZLIB;
|
||||
constexpr int MAX = _32KiB;
|
||||
} // namespace GZIP
|
||||
|
||||
constexpr int ADD_TO_ENABLE_GZIP = 32;
|
||||
|
||||
namespace ZLIB_OR_GZIP {
|
||||
constexpr int AUTO = ZLIB::AUTO + ADD_TO_ENABLE_GZIP;
|
||||
constexpr int _256B = ZLIB::_256B + ADD_TO_ENABLE_GZIP;
|
||||
constexpr int MIN = _256B;
|
||||
constexpr int _512B = ZLIB::_512B + ADD_TO_ENABLE_GZIP;
|
||||
constexpr int _1KiB = ZLIB::_1KiB + ADD_TO_ENABLE_GZIP;
|
||||
constexpr int _2KiB = ZLIB::_2KiB + ADD_TO_ENABLE_GZIP;
|
||||
constexpr int _4KiB = ZLIB::_4KiB + ADD_TO_ENABLE_GZIP;
|
||||
constexpr int _8KiB = ZLIB::_8KiB + ADD_TO_ENABLE_GZIP;
|
||||
constexpr int _16KiB = ZLIB::_16KiB + ADD_TO_ENABLE_GZIP;
|
||||
constexpr int _32KiB = ZLIB::_32KiB + ADD_TO_ENABLE_GZIP;
|
||||
constexpr int MAX = _32KiB;
|
||||
} // namespace ZLIB_OR_GZIP
|
||||
} // namespace WindowBits
|
||||
|
||||
class z_stream_wrapper
|
||||
: public z_stream
|
||||
{
|
||||
public:
|
||||
z_stream_wrapper(bool _is_input, int _level, int _window_bits)
|
||||
: z_stream(), is_input(_is_input) // value-initialize base z_stream to zeros
|
||||
{
|
||||
this->zalloc = nullptr;//Z_NULL
|
||||
this->zfree = nullptr;//Z_NULL
|
||||
this->opaque = nullptr;//Z_NULL
|
||||
int ret;
|
||||
if (is_input)
|
||||
{
|
||||
this->avail_in = 0;
|
||||
this->next_in = nullptr;//Z_NULL
|
||||
#ifdef MINIZ
|
||||
// miniz expects MZ_DEFAULT_WINDOW_BITS (15) or -15 for raw; avoid zlib-only auto-detect flags
|
||||
ret = inflateInit2(this, _window_bits ? _window_bits : WindowBits::ZLIB::MAX);
|
||||
#else
|
||||
ret = inflateInit2(this, _window_bits ? _window_bits : WindowBits::ZLIB_OR_GZIP::MAX);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef MINIZ
|
||||
ret = deflateInit2(this, _level, Z_DEFLATED, _window_bits ? _window_bits : WindowBits::ZLIB::MAX, 8, Z_DEFAULT_STRATEGY);
|
||||
#else
|
||||
ret = deflateInit2(this, _level, Z_DEFLATED, _window_bits ? _window_bits : WindowBits::GZIP::MAX, 8, Z_DEFAULT_STRATEGY);
|
||||
#endif
|
||||
}
|
||||
if (ret != Z_OK) throw Exception(this, ret);
|
||||
}
|
||||
~z_stream_wrapper()
|
||||
{
|
||||
if (is_input)
|
||||
{
|
||||
inflateEnd(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
deflateEnd(this);
|
||||
}
|
||||
}
|
||||
private:
|
||||
bool is_input;
|
||||
}; // class z_stream_wrapper
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class istreambuf
|
||||
: public std::streambuf
|
||||
{
|
||||
public:
|
||||
istreambuf(std::streambuf * _sbuf_p,
|
||||
std::size_t _buff_size = default_buff_size, bool _auto_detect = true, int _window_bits = 0)
|
||||
: sbuf_p(_sbuf_p),
|
||||
in_buff(),
|
||||
in_buff_start(nullptr),
|
||||
in_buff_end(nullptr),
|
||||
out_buff(),
|
||||
zstrm_p(nullptr),
|
||||
buff_size(_buff_size),
|
||||
auto_detect(_auto_detect),
|
||||
auto_detect_run(false),
|
||||
is_text(false),
|
||||
window_bits(_window_bits)
|
||||
{
|
||||
assert(sbuf_p);
|
||||
in_buff = std::unique_ptr<char[]>(new char[buff_size]);
|
||||
in_buff_start = in_buff.get();
|
||||
in_buff_end = in_buff.get();
|
||||
out_buff = std::unique_ptr<char[]>(new char[buff_size]);
|
||||
setg(out_buff.get(), out_buff.get(), out_buff.get());
|
||||
}
|
||||
|
||||
istreambuf(const istreambuf &) = delete;
|
||||
istreambuf & operator = (const istreambuf &) = delete;
|
||||
|
||||
pos_type seekoff(off_type off, std::ios_base::seekdir dir,
|
||||
std::ios_base::openmode which) override
|
||||
{
|
||||
if (off != 0 || dir != std::ios_base::cur) {
|
||||
return std::streambuf::seekoff(off, dir, which);
|
||||
}
|
||||
|
||||
if (!zstrm_p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return static_cast<long int>(zstrm_p->total_out - static_cast<uLong>(in_avail()));
|
||||
}
|
||||
|
||||
struct ZlibHeader {
|
||||
// Based on RFC 1950 (https://datatracker.ietf.org/doc/html/rfc1950#section-2.2)
|
||||
// See also:
|
||||
// http://stackoverflow.com/questions/9050260/what-does-a-zlib-header-look-like
|
||||
|
||||
// 0 to 7, log2 of the windowSize in bytes
|
||||
uint8_t cminfo;
|
||||
// always 8, the compression method
|
||||
uint8_t cm;
|
||||
// 0 to 3, the compression level, higher is more compressed
|
||||
uint8_t flevel;
|
||||
// usually 0, true if a preset dictionary is provided after the header
|
||||
bool fdict;
|
||||
// 0 to 31, checksum: ((cminfo * 16 + cm) * 256 + flevel * 32 + fdict * 16 + fcheck) % 31 = 0
|
||||
uint8_t fcheck;
|
||||
private:
|
||||
uint16_t total;
|
||||
|
||||
public:
|
||||
ZlibHeader(const uint8_t cmf, const uint8_t flg) {
|
||||
// the top 4 bits
|
||||
cminfo = cmf >> 4;
|
||||
// the bottom 4 bits
|
||||
cm = cmf & 0xf;
|
||||
|
||||
// the top 2 bits
|
||||
flevel = flg >> 6;
|
||||
// the 3rd top bit
|
||||
fdict = flg & 0x20;
|
||||
// the bottom 5 bits
|
||||
fcheck = flg & 0x1f;
|
||||
|
||||
// reinterpret as integer in MSB order
|
||||
total = cmf * 256 + flg;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isValid() const noexcept {
|
||||
return cm == 8 && total % 31 == 0;
|
||||
}
|
||||
};
|
||||
|
||||
static bool is_compressed(const char* const buffer, const char* const end) {
|
||||
// Buffer too short
|
||||
if (buffer + 2 > end)
|
||||
return false;
|
||||
|
||||
const auto b0 = static_cast<uint8_t>(buffer[0]);
|
||||
const auto b1 = static_cast<uint8_t>(buffer[1]);
|
||||
|
||||
// Check for Gzip magic numbers
|
||||
// http://en.wikipedia.org/wiki/Gzip
|
||||
if (b0 == 0x1F && b1 == 0x8B)
|
||||
return true;
|
||||
if (ZlibHeader(b0, b1).isValid())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::streambuf::int_type underflow() override
|
||||
{
|
||||
if (this->gptr() == this->egptr())
|
||||
{
|
||||
// pointers for free region in output buffer
|
||||
char * out_buff_free_start = out_buff.get();
|
||||
int tries = 0;
|
||||
do
|
||||
{
|
||||
if (++tries > 1000) {
|
||||
throw std::ios_base::failure("Failed to fill buffer after 1000 tries");
|
||||
}
|
||||
|
||||
// read more input if none available
|
||||
if (in_buff_start == in_buff_end)
|
||||
{
|
||||
// empty input buffer: refill from the start
|
||||
in_buff_start = in_buff.get();
|
||||
std::streamsize sz = sbuf_p->sgetn(in_buff.get(), static_cast<std::streamsize>(buff_size));
|
||||
in_buff_end = in_buff_start + sz;
|
||||
if (in_buff_end == in_buff_start) break; // end of input
|
||||
}
|
||||
// auto detect if the stream contains text or deflate data
|
||||
if (auto_detect && ! auto_detect_run)
|
||||
{
|
||||
auto_detect_run = true;
|
||||
is_text = !is_compressed(in_buff_start, in_buff_end);
|
||||
}
|
||||
if (is_text)
|
||||
{
|
||||
// simply swap in_buff and out_buff, and adjust pointers
|
||||
assert(in_buff_start == in_buff.get());
|
||||
std::swap(in_buff, out_buff);
|
||||
out_buff_free_start = in_buff_end;
|
||||
in_buff_start = in_buff.get();
|
||||
in_buff_end = in_buff.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
// run inflate() on input
|
||||
if (! zstrm_p) zstrm_p = std::unique_ptr<detail::z_stream_wrapper>(new detail::z_stream_wrapper(true, Z_DEFAULT_COMPRESSION, window_bits));
|
||||
zstrm_p->next_in = reinterpret_cast< decltype(zstrm_p->next_in) >(in_buff_start);
|
||||
zstrm_p->avail_in = uint32_t(in_buff_end - in_buff_start);
|
||||
zstrm_p->next_out = reinterpret_cast< decltype(zstrm_p->next_out) >(out_buff_free_start);
|
||||
zstrm_p->avail_out = uint32_t((out_buff.get() + buff_size) - out_buff_free_start);
|
||||
int ret = inflate(zstrm_p.get(), Z_NO_FLUSH);
|
||||
// process return code
|
||||
if (ret != Z_OK && ret != Z_STREAM_END) throw Exception(zstrm_p.get(), ret);
|
||||
// update in&out pointers following inflate()
|
||||
in_buff_start = reinterpret_cast< decltype(in_buff_start) >(
|
||||
#ifdef MINIZ
|
||||
const_cast< unsigned char* >(zstrm_p->next_in)
|
||||
#else
|
||||
zstrm_p->next_in
|
||||
#endif
|
||||
);
|
||||
in_buff_end = in_buff_start + zstrm_p->avail_in;
|
||||
out_buff_free_start = reinterpret_cast< decltype(out_buff_free_start) >(zstrm_p->next_out);
|
||||
assert(out_buff_free_start + zstrm_p->avail_out == out_buff.get() + buff_size);
|
||||
|
||||
if (ret == Z_STREAM_END) {
|
||||
// if stream ended, deallocate inflator
|
||||
zstrm_p.reset();
|
||||
}
|
||||
}
|
||||
} while (out_buff_free_start == out_buff.get());
|
||||
// 2 exit conditions:
|
||||
// - end of input: there might or might not be output available
|
||||
// - out_buff_free_start != out_buff: output available
|
||||
this->setg(out_buff.get(), out_buff.get(), out_buff_free_start);
|
||||
}
|
||||
return this->gptr() == this->egptr()
|
||||
? traits_type::eof()
|
||||
: traits_type::to_int_type(*this->gptr());
|
||||
}
|
||||
private:
|
||||
std::streambuf * sbuf_p;
|
||||
std::unique_ptr<char[]> in_buff;
|
||||
char * in_buff_start;
|
||||
char * in_buff_end;
|
||||
std::unique_ptr<char[]> out_buff;
|
||||
std::unique_ptr<detail::z_stream_wrapper> zstrm_p;
|
||||
std::size_t buff_size;
|
||||
bool auto_detect;
|
||||
bool auto_detect_run;
|
||||
bool is_text;
|
||||
int window_bits;
|
||||
|
||||
}; // class istreambuf
|
||||
|
||||
class ostreambuf
|
||||
: public std::streambuf
|
||||
{
|
||||
public:
|
||||
ostreambuf(std::streambuf * _sbuf_p,
|
||||
std::size_t _buff_size = default_buff_size, int _level = Z_DEFAULT_COMPRESSION, int _window_bits = 0)
|
||||
: sbuf_p(_sbuf_p),
|
||||
in_buff(),
|
||||
out_buff(),
|
||||
zstrm_p(new detail::z_stream_wrapper(false, _level, _window_bits)),
|
||||
buff_size(_buff_size),
|
||||
compress(_level != Z_NO_COMPRESSION)
|
||||
{
|
||||
assert(sbuf_p);
|
||||
if (!compress)
|
||||
return;
|
||||
in_buff = std::unique_ptr<char[]>(new char[buff_size]);
|
||||
out_buff = std::unique_ptr<char[]>(new char[buff_size]);
|
||||
setp(in_buff.get(), in_buff.get() + buff_size);
|
||||
}
|
||||
|
||||
ostreambuf(const ostreambuf &) = delete;
|
||||
ostreambuf & operator = (const ostreambuf &) = delete;
|
||||
|
||||
int deflate_loop(int flush)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
zstrm_p->next_out = reinterpret_cast< decltype(zstrm_p->next_out) >(out_buff.get());
|
||||
zstrm_p->avail_out = uint32_t(buff_size);
|
||||
int ret = deflate(zstrm_p.get(), flush);
|
||||
if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) {
|
||||
failed = true;
|
||||
throw Exception(zstrm_p.get(), ret);
|
||||
}
|
||||
std::streamsize sz = sbuf_p->sputn(out_buff.get(), reinterpret_cast< decltype(out_buff.get()) >(zstrm_p->next_out) - out_buff.get());
|
||||
if (sz != reinterpret_cast< decltype(out_buff.get()) >(zstrm_p->next_out) - out_buff.get())
|
||||
{
|
||||
// there was an error in the sink stream
|
||||
return -1;
|
||||
}
|
||||
if (ret == Z_STREAM_END || ret == Z_BUF_ERROR || sz == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual ~ostreambuf()
|
||||
{
|
||||
// flush the zlib stream
|
||||
//
|
||||
// NOTE: Errors here (sync() return value not 0) are ignored, because we
|
||||
// cannot throw in a destructor. This mirrors the behaviour of
|
||||
// std::basic_filebuf::~basic_filebuf(). To see an exception on error,
|
||||
// close the ofstream with an explicit call to close(), and do not rely
|
||||
// on the implicit call in the destructor.
|
||||
//
|
||||
if (!failed && compress) try {
|
||||
sync();
|
||||
} catch (...) {}
|
||||
}
|
||||
std::streambuf::int_type overflow(std::streambuf::int_type c = traits_type::eof()) override
|
||||
{
|
||||
if (!compress)
|
||||
{
|
||||
if (!traits_type::eq_int_type(c, traits_type::eof()))
|
||||
return sbuf_p->sputc(char_type(c));
|
||||
return traits_type::not_eof(c);
|
||||
}
|
||||
|
||||
zstrm_p->next_in = reinterpret_cast< decltype(zstrm_p->next_in) >(pbase());
|
||||
zstrm_p->avail_in = uint32_t(pptr() - pbase());
|
||||
while (zstrm_p->avail_in > 0)
|
||||
{
|
||||
int r = deflate_loop(Z_NO_FLUSH);
|
||||
if (r != 0)
|
||||
{
|
||||
setp(nullptr, nullptr);
|
||||
return traits_type::eof();
|
||||
}
|
||||
}
|
||||
setp(in_buff.get(), in_buff.get() + buff_size);
|
||||
return traits_type::eq_int_type(c, traits_type::eof()) ? traits_type::eof() : sputc(char_type(c));
|
||||
}
|
||||
int sync() override
|
||||
{
|
||||
if (!compress)
|
||||
return sbuf_p->pubsync();
|
||||
|
||||
// first, call overflow to clear in_buff
|
||||
overflow();
|
||||
if (! pptr()) return -1;
|
||||
// then, call deflate asking to finish the zlib stream
|
||||
zstrm_p->next_in = nullptr;
|
||||
zstrm_p->avail_in = 0;
|
||||
if (deflate_loop(Z_FINISH) != 0) return -1;
|
||||
deflateReset(zstrm_p.get());
|
||||
return 0;
|
||||
}
|
||||
private:
|
||||
std::streambuf * sbuf_p = nullptr;
|
||||
std::unique_ptr<char[]> in_buff;
|
||||
std::unique_ptr<char[]> out_buff;
|
||||
std::unique_ptr<detail::z_stream_wrapper> zstrm_p;
|
||||
std::size_t buff_size;
|
||||
bool failed = false;
|
||||
bool compress;
|
||||
|
||||
}; // class ostream
|
||||
|
||||
class istream
|
||||
: public std::istream
|
||||
{
|
||||
public:
|
||||
istream(std::istream & is,
|
||||
std::size_t _buff_size = default_buff_size, bool _auto_detect = true, int _window_bits = 0)
|
||||
: std::istream(new istreambuf(is.rdbuf(), _buff_size, _auto_detect, _window_bits))
|
||||
{
|
||||
exceptions(std::ios_base::badbit);
|
||||
}
|
||||
explicit istream(std::streambuf * sbuf_p,
|
||||
std::size_t _buff_size = default_buff_size, bool _auto_detect = true, int _window_bits = 0)
|
||||
: std::istream(new istreambuf(sbuf_p, _buff_size, _auto_detect, _window_bits))
|
||||
{
|
||||
exceptions(std::ios_base::badbit);
|
||||
}
|
||||
virtual ~istream()
|
||||
{
|
||||
delete rdbuf();
|
||||
}
|
||||
}; // class istream
|
||||
|
||||
class ostream
|
||||
: public std::ostream
|
||||
{
|
||||
public:
|
||||
ostream(std::ostream & os,
|
||||
std::size_t _buff_size = default_buff_size, int _level = Z_DEFAULT_COMPRESSION, int _window_bits = 0)
|
||||
: std::ostream(new ostreambuf(os.rdbuf(), _buff_size, _level, _window_bits))
|
||||
{
|
||||
exceptions(std::ios_base::badbit);
|
||||
}
|
||||
explicit ostream(std::streambuf * sbuf_p,
|
||||
std::size_t _buff_size = default_buff_size, int _level = Z_DEFAULT_COMPRESSION, int _window_bits = 0)
|
||||
: std::ostream(new ostreambuf(sbuf_p, _buff_size, _level, _window_bits))
|
||||
{
|
||||
exceptions(std::ios_base::badbit);
|
||||
}
|
||||
virtual ~ostream()
|
||||
{
|
||||
delete rdbuf();
|
||||
}
|
||||
}; // class ostream
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template < typename FStream_Type >
|
||||
struct strict_fstream_holder
|
||||
{
|
||||
strict_fstream_holder(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
|
||||
: _fs(filename, mode)
|
||||
{}
|
||||
strict_fstream_holder() = default;
|
||||
FStream_Type _fs {};
|
||||
}; // class strict_fstream_holder
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class ifstream
|
||||
: private detail::strict_fstream_holder< strict_fstream::ifstream >,
|
||||
public std::istream
|
||||
{
|
||||
public:
|
||||
explicit ifstream(const std::string filename, std::ios_base::openmode mode = std::ios_base::in, size_t buff_size = default_buff_size)
|
||||
: detail::strict_fstream_holder< strict_fstream::ifstream >(filename, mode),
|
||||
std::istream(new istreambuf(_fs.rdbuf(), buff_size))
|
||||
{
|
||||
exceptions(std::ios_base::badbit);
|
||||
}
|
||||
explicit ifstream(): detail::strict_fstream_holder< strict_fstream::ifstream >(), std::istream(new istreambuf(_fs.rdbuf())){}
|
||||
void close() {
|
||||
_fs.close();
|
||||
}
|
||||
#ifdef CAN_MOVE_IOSTREAM
|
||||
void open(const std::string filename, std::ios_base::openmode mode = std::ios_base::in) {
|
||||
_fs.open(filename, mode);
|
||||
std::istream::operator=(std::istream(new istreambuf(_fs.rdbuf())));
|
||||
}
|
||||
#endif
|
||||
bool is_open() const {
|
||||
return _fs.is_open();
|
||||
}
|
||||
virtual ~ifstream()
|
||||
{
|
||||
if (_fs.is_open()) close();
|
||||
if (rdbuf()) delete rdbuf();
|
||||
}
|
||||
|
||||
/// Return the position within the compressed file (wrapped filestream)
|
||||
std::streampos compressed_tellg()
|
||||
{
|
||||
return _fs.tellg();
|
||||
}
|
||||
}; // class ifstream
|
||||
|
||||
class ofstream
|
||||
: private detail::strict_fstream_holder< strict_fstream::ofstream >,
|
||||
public std::ostream
|
||||
{
|
||||
public:
|
||||
explicit ofstream(const std::string filename, std::ios_base::openmode mode = std::ios_base::out,
|
||||
int level = Z_DEFAULT_COMPRESSION, size_t buff_size = default_buff_size)
|
||||
: detail::strict_fstream_holder< strict_fstream::ofstream >(filename, mode | std::ios_base::binary),
|
||||
std::ostream(new ostreambuf(_fs.rdbuf(), buff_size, level))
|
||||
{
|
||||
exceptions(std::ios_base::badbit);
|
||||
}
|
||||
explicit ofstream(): detail::strict_fstream_holder< strict_fstream::ofstream >(), std::ostream(new ostreambuf(_fs.rdbuf())){}
|
||||
void close() {
|
||||
std::ostream::flush();
|
||||
_fs.close();
|
||||
}
|
||||
#ifdef CAN_MOVE_IOSTREAM
|
||||
void open(const std::string filename, std::ios_base::openmode mode = std::ios_base::out, int level = Z_DEFAULT_COMPRESSION) {
|
||||
flush();
|
||||
_fs.open(filename, mode | std::ios_base::binary);
|
||||
rdbuf(new ostreambuf(_fs.rdbuf(), default_buff_size, level));
|
||||
}
|
||||
#endif
|
||||
bool is_open() const {
|
||||
return _fs.is_open();
|
||||
}
|
||||
ofstream& flush() {
|
||||
std::ostream::flush();
|
||||
_fs.flush();
|
||||
return *this;
|
||||
}
|
||||
virtual ~ofstream()
|
||||
{
|
||||
if (_fs.is_open()) close();
|
||||
if (rdbuf()) delete rdbuf();
|
||||
}
|
||||
|
||||
// Return the position within the compressed file (wrapped filestream)
|
||||
std::streampos compressed_tellp()
|
||||
{
|
||||
return _fs.tellp();
|
||||
}
|
||||
}; // class ofstream
|
||||
|
||||
} // namespace zstr
|
||||
|
||||
|
|
@ -23,11 +23,15 @@ int catFile(const int argc, const char** argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
const std::string input = argv[3];
|
||||
const std::string dir = input.substr(0, 2);
|
||||
const std::string file = input.substr(2);
|
||||
const std::string_view input {argv[3]};
|
||||
|
||||
zstr::ifstream inputFile("./.git/objects/" + dir + "/" + file, std::ofstream::binary);
|
||||
const std::filesystem::path dir { input.substr(0, 2) };
|
||||
|
||||
const std::string_view file { std::string_view(input.substr(2)) };
|
||||
|
||||
const std::filesystem::path fullPath{ std::filesystem::current_path() / ".git/objects/" / dir / file };
|
||||
|
||||
zstr::ifstream inputFile(fullPath.string(), std::ofstream::binary);
|
||||
|
||||
if (!inputFile.is_open())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include "sha1.hpp"
|
||||
#include "zstr.hpp"
|
||||
#include "date/tz.h"
|
||||
#include "tz.h"
|
||||
|
||||
int commitTree(int argc, const char** argv)
|
||||
{
|
||||
|
|
@ -48,12 +48,13 @@ int commitTree(int argc, const char** argv)
|
|||
}
|
||||
|
||||
// get author info
|
||||
if (std::filesystem::exists("./.git/author.txt"))
|
||||
const std::filesystem::path authorInfoPath{ std::filesystem::current_path() / ".git/author.txt" };
|
||||
if (std::filesystem::exists(authorInfoPath))
|
||||
{
|
||||
std::ifstream authorInfo("./.git/author.txt");
|
||||
std::ifstream authorInfo(authorInfoPath);
|
||||
if (!authorInfo)
|
||||
{
|
||||
std::cerr << "\n\n'/.git/author.txt exists but could not be opened\n\n";
|
||||
std::cerr << "\n\n" << authorInfoPath << "exists but could not be opened\n\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -80,17 +81,17 @@ int commitTree(int argc, const char** argv)
|
|||
|
||||
if (input == 'Y')
|
||||
{
|
||||
std::ofstream authorFile("./.git/author.txt");
|
||||
std::ofstream authorFile(authorInfoPath);
|
||||
if (!authorFile)
|
||||
{
|
||||
std::cerr << "\n\ncould not write author info to '/.git/author.txt'\n\n";
|
||||
std::cerr << "\n\ncould not write author info to " << authorInfoPath << "\n\n";
|
||||
authorFile.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
authorFile << authorName << "\n" << authorEmail << "\n";
|
||||
authorFile.close();
|
||||
std::cout << "successfully wrote author info to '/.git/author.txt\n";
|
||||
std::cout << "successfully wrote author info to " << authorInfoPath << "\n";
|
||||
}
|
||||
}
|
||||
}// end get author info
|
||||
|
|
@ -99,26 +100,37 @@ int commitTree(int argc, const char** argv)
|
|||
std::ostringstream tzOffset;
|
||||
tzOffset << timeZoneLocal.get_info().offset;
|
||||
|
||||
//assume commiter and author are the same
|
||||
commitBody.append("author " + authorName + " <" + authorEmail + "> " + std::to_string(timeZoneLocal.get_local_time().time_since_epoch().count()) + " " + tzOffset.str().substr(0,5) + "\n") ;//use substr() to exclude the char that defines the unit of measurement
|
||||
commitBody.append("commiter " + authorName + " <" + authorEmail + "> " + std::to_string(timeZoneLocal.get_local_time().time_since_epoch().count()) + " " + tzOffset.str().substr(0,5) + "\n\n") ;
|
||||
const auto epochSeconds = std::chrono::duration_cast<std::chrono::seconds>(timeZoneLocal.get_local_time().time_since_epoch()).count();
|
||||
|
||||
auto authorInfo = [&](const char* input) -> std::string {
|
||||
std::ostringstream ss;
|
||||
ss << input << ' '
|
||||
<< authorName << " <" << authorEmail << "> "
|
||||
<< epochSeconds << ' ' << tzOffset.str().substr(0, 5) << '\n';
|
||||
return ss.str();
|
||||
};
|
||||
|
||||
commitBody.append(authorInfo("author"));
|
||||
commitBody.append(authorInfo("committer"));
|
||||
commitBody.append("\n");
|
||||
commitBody.append(commitMessage);
|
||||
|
||||
commitHeader.append("commit " + std::to_string(commitBody.size()) + '\0');
|
||||
|
||||
std::string commitObject = commitHeader + commitBody;
|
||||
std::string commitObject {commitHeader + commitBody};
|
||||
|
||||
SHA1 checksum;
|
||||
checksum.update(commitObject);
|
||||
const std::string hash = checksum.final();
|
||||
const std::string hashDir = "./.git/objects/" + hash.substr(0,2);
|
||||
const std::filesystem::path hashDir{ std::filesystem::current_path() / ".git" / "objects" / hash.substr(0,2) };
|
||||
const std::filesystem::path objectFullPath{ hashDir / hash.substr(2) };
|
||||
|
||||
if (!std::filesystem::exists(hashDir))
|
||||
{
|
||||
std::filesystem::create_directory(hashDir);
|
||||
}
|
||||
|
||||
zstr::ofstream outputFile(hashDir + "/" + hash.substr(2));
|
||||
zstr::ofstream outputFile(objectFullPath.string());
|
||||
outputFile.write(commitObject.c_str(), commitObject.size());
|
||||
|
||||
outputFile.close();
|
||||
|
|
|
|||
|
|
@ -11,7 +11,11 @@
|
|||
|
||||
int hashObject(const int argc, const char** argv)
|
||||
{
|
||||
std::filesystem::create_directory("./.git/objects");
|
||||
if (!std::filesystem::exists(std::filesystem::current_path() / ".git" / "objects"))
|
||||
{
|
||||
std::filesystem::create_directory(std::filesystem::current_path() / ".git" / "objects");
|
||||
}
|
||||
|
||||
if (argc < 4)
|
||||
{
|
||||
std::cerr << "please provide proper arguments. example '-w <object>'.\n";
|
||||
|
|
@ -26,7 +30,7 @@ int hashObject(const int argc, const char** argv)
|
|||
}
|
||||
|
||||
const std::string path = argv[3];
|
||||
std::ifstream inputFile("./" + path);
|
||||
std::ifstream inputFile(std::filesystem::current_path() / path);
|
||||
if (!inputFile.is_open())
|
||||
{
|
||||
std::cerr << "Could not open file\n";
|
||||
|
|
@ -41,14 +45,15 @@ int hashObject(const int argc, const char** argv)
|
|||
const std::string hash = checksum.final();
|
||||
std::cout << hash << "\n";
|
||||
|
||||
const std::string hashDir = "./.git/objects/" + hash.substr(0,2);
|
||||
const std::filesystem::path hashDir { std::filesystem::current_path() / ".git" / "objects" / hash.substr(0,2) };
|
||||
const std::filesystem::path objectFullPath { hashDir / hash.substr(2) };
|
||||
|
||||
if (!std::filesystem::exists(hashDir))
|
||||
{
|
||||
std::filesystem::create_directory(hashDir);
|
||||
}
|
||||
|
||||
zstr::ofstream outputFile(hashDir + "/" + hash.substr(2));
|
||||
zstr::ofstream outputFile(objectFullPath.string());
|
||||
outputFile.write(objectData.c_str(), objectData.size());
|
||||
|
||||
inputFile.close();
|
||||
|
|
|
|||
|
|
@ -9,16 +9,17 @@ int init()
|
|||
{
|
||||
try
|
||||
{
|
||||
std::filesystem::create_directory(".git");
|
||||
std::filesystem::create_directory(".git/objects");
|
||||
std::filesystem::create_directory(".git/refs");
|
||||
std::filesystem::path gitDir(std::filesystem::current_path() / ".git");
|
||||
std::filesystem::create_directory(gitDir);
|
||||
std::filesystem::create_directory(gitDir / "objects");
|
||||
std::filesystem::create_directory(gitDir / "refs");
|
||||
|
||||
std::ofstream headFile(".git/HEAD");
|
||||
std::ofstream headFile(gitDir / "HEAD");
|
||||
if (headFile.is_open()) {
|
||||
headFile << "ref: refs/heads/main\n";
|
||||
headFile.close();
|
||||
} else {
|
||||
std::cerr << "Failed to create .git/HEAD file.\n";
|
||||
std::cerr << "Failed to create " << std::filesystem::path(gitDir / "HEAD") << " file.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,11 +22,15 @@ int lsTree(const int argc, const char** argv)
|
|||
nameOnly = true;
|
||||
}
|
||||
|
||||
const std::string input = nameOnly ? argv[3] : argv[2];
|
||||
const std::string dir = input.substr(0, 2);
|
||||
const std::string file = input.substr(2);
|
||||
const std::string input {nameOnly ? argv[3] : argv[2]};
|
||||
|
||||
zstr::ifstream inputFile("./.git/objects/" + dir + "/" + file, std::ofstream::binary);
|
||||
const std::filesystem::path dir {input.substr(0, 2)};
|
||||
|
||||
const std::string file {input.substr(2)};
|
||||
|
||||
const std::filesystem::path fullPath{ std::filesystem::current_path() / ".git/objects/" / dir / file };
|
||||
|
||||
zstr::ifstream inputFile(fullPath.string(), std::ofstream::binary);
|
||||
if (!inputFile.is_open())
|
||||
{
|
||||
std::cerr << "Could not open file\n";
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@
|
|||
#include <fstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <cstdint>
|
||||
|
||||
#include "sha1.hpp"
|
||||
#include "zstr.hpp"
|
||||
|
|
@ -81,9 +85,13 @@ std::string hashDir(const std::string& path)
|
|||
std::string fileContent {std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
|
||||
|
||||
SHA1 checksum;
|
||||
checksum.update("blob " + std::to_string(fileContent.size()) + '\0' + fileContent);
|
||||
std::string blob = "blob " + std::to_string(fileContent.size());
|
||||
blob.append("\0");
|
||||
blob.append(fileContent);
|
||||
|
||||
nodes.emplace_back(object.path().filename(), 100644, checksum.final());
|
||||
checksum.update(blob);
|
||||
|
||||
nodes.emplace_back(object.path().filename().string(), 100644, checksum.final());
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
|
@ -105,22 +113,25 @@ std::string hashDir(const std::string& path)
|
|||
}
|
||||
}
|
||||
|
||||
treeObject = "tree " + std::to_string(treeObject.size()) + '\0' + treeObject;
|
||||
std::string header{ "tree " + std::to_string(treeObject.size()) };
|
||||
header.push_back('\0');
|
||||
std::string finalObject = header + treeObject;
|
||||
|
||||
SHA1 checksum;
|
||||
checksum.update(finalObject);
|
||||
|
||||
SHA1 checksum;
|
||||
checksum.update(treeObject);
|
||||
return checksum.final();
|
||||
}
|
||||
|
||||
int writeTree(const char** argv)
|
||||
{
|
||||
if (!std::filesystem::exists("./.git/objects"))
|
||||
std::filesystem::create_directory("./.git/objects");
|
||||
if (!std::filesystem::exists(std::filesystem::current_path() / ".git" / "objects"))
|
||||
std::filesystem::create_directory(std::filesystem::current_path() / ".git" / "objects");
|
||||
|
||||
std::vector<node> nodes;
|
||||
|
||||
|
||||
for (const auto& object : std::filesystem::directory_iterator("./"))
|
||||
for (const auto& object : std::filesystem::directory_iterator(std::filesystem::current_path()))
|
||||
{
|
||||
if (object.path().filename() == ".git") continue;
|
||||
|
||||
|
|
@ -136,7 +147,7 @@ int writeTree(const char** argv)
|
|||
SHA1 checksum;
|
||||
checksum.update("blob " + std::to_string(fileContent.size()) + '\0' + fileContent);
|
||||
|
||||
nodes.emplace_back(object.path().filename(), 100644, checksum.final());
|
||||
nodes.emplace_back(object.path().filename().string(), 100644, checksum.final());
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
|
@ -151,7 +162,7 @@ int writeTree(const char** argv)
|
|||
for (const auto&[name, mode, hash] : nodes)
|
||||
{
|
||||
treeObject += std::to_string(mode) + " " + name + '\0';
|
||||
for (int i = 0; i < 39; i += 2)
|
||||
for (int i = 0; i < 40; i += 2)
|
||||
{
|
||||
uint8_t byte = hexToRaw(hash.at(i), hash.at(i+1));
|
||||
|
||||
|
|
@ -159,23 +170,27 @@ int writeTree(const char** argv)
|
|||
}
|
||||
}
|
||||
|
||||
treeObject = "tree " + std::to_string(treeObject.size()) + '\0' + treeObject;
|
||||
std::string header {"tree " + std::to_string(treeObject.size())};
|
||||
header.push_back('\0');
|
||||
std::string finalObject = header + treeObject;
|
||||
|
||||
SHA1 checksum;
|
||||
checksum.update(treeObject);
|
||||
checksum.update(finalObject);
|
||||
|
||||
const std::string hash = checksum.final();
|
||||
std::cout << hash << "\n";
|
||||
|
||||
const std::string hashDir = "./.git/objects/" + hash.substr(0,2);
|
||||
const std::filesystem::path hashDir {std::filesystem::current_path() / ".git" / "objects" / hash.substr(0,2)};
|
||||
|
||||
const std::filesystem::path objectFullPath {hashDir / hash.substr(2)};
|
||||
|
||||
if (!std::filesystem::exists(hashDir))
|
||||
{
|
||||
std::filesystem::create_directory(hashDir);
|
||||
}
|
||||
|
||||
zstr::ofstream outputFile(hashDir + "/" + hash.substr(2));
|
||||
outputFile.write(treeObject.c_str(), treeObject.size());
|
||||
zstr::ofstream outputFile(objectFullPath.string());
|
||||
outputFile.write(finalObject.c_str(), finalObject.size());
|
||||
|
||||
outputFile.close();
|
||||
|
||||
|
|
|
|||
|
|
@ -39,10 +39,6 @@ int main(const int argc, const char** argv)
|
|||
{
|
||||
return commitTree(argc, argv);
|
||||
}
|
||||
else if (command == "clone")
|
||||
{
|
||||
return clone(argc, argv);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << argv[0] << " is not a valid command\n";
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue