added support for windows

This commit is contained in:
Joseph Aquino 2025-12-26 05:55:04 -05:00
parent 3f58464c6c
commit 4affce9f0f
19 changed files with 26311 additions and 68 deletions

6
.gitignore vendored
View File

@ -387,4 +387,8 @@ Makefile
.idea/
cmake-build*/
cmake-build*/
build/
bin/

View File

@ -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_include_directories(basic-vcs PRIVATE include)
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()

4
generate-solution.bat Normal file
View File

@ -0,0 +1,4 @@
mkdir build
cd build
cmake -S .. -B .
PAUSE

View File

@ -1,7 +1,6 @@
#pragma once
#include "cat-file.h"
#include "clone.h"
#include "hash-object.h"
#include "init.h"
#include "ls-tree.h"

8373
include/date.h Normal file

File diff suppressed because it is too large Load Diff

1507
include/miniz.h Normal file

File diff suppressed because it is too large Load Diff

237
include/strict_fstream.hpp Normal file
View File

@ -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

2809
include/tz.h Normal file

File diff suppressed because it is too large Load Diff

315
include/tz_private.h Normal file
View File

@ -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

673
include/zstr.hpp Normal file
View File

@ -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

View File

@ -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())
{

View File

@ -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();

View File

@ -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();

View File

@ -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;
}

View File

@ -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";

View File

@ -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();

View File

@ -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";

7909
src/miniz.c Normal file

File diff suppressed because it is too large Load Diff

4377
src/tz.cpp Normal file

File diff suppressed because it is too large Load Diff