/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2019 whitequark * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ // The reason the -path mode of connect_rpc uses byte-oriented and not message-oriented sockets, even though // it is a message-oriented interface, is that the system can place various limits on the message size, which // are not always transparent or easy to change. Given that generated HDL code get be extremely large, it is // unwise to rely on those limits being large enough, and using byte-oriented sockets is guaranteed to work. #ifndef _WIN32 #include #include #include #include #include extern char **environ; #endif #include "libs/json11/json11.hpp" #include "libs/sha1/sha1.h" #include "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN #if defined(_WIN32) static std::wstring str2wstr(const std::string &in) { if(in == "") return L""; std::wstring out; out.resize(MultiByteToWideChar(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpMultiByteStr=*/&in[0], /*cbMultiByte=*/(int)in.length(), /*lpWideCharStr=*/NULL, /*cchWideChar=*/0)); int written = MultiByteToWideChar(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpMultiByteStr=*/&in[0], /*cbMultiByte=*/(int)in.length(), /*lpWideCharStr=*/&out[0], /*cchWideChar=*/(int)out.length()); log_assert(written == (int)out.length()); return out; } static std::string wstr2str(const std::wstring &in) { if(in == L"") return ""; std::string out; out.resize(WideCharToMultiByte(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpWideCharStr=*/&in[0], /*cchWideChar=*/(int)in.length(), /*lpMultiByteStr=*/NULL, /*cbMultiByte=*/0, /*lpDefaultChar=*/NULL, /*lpUsedDefaultChar=*/NULL)); int written = WideCharToMultiByte(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpWideCharStr=*/&in[0], /*cchWideChar=*/(int)in.length(), /*lpMultiByteStr=*/&out[0], /*cbMultiByte=*/(int)out.length(), /*lpDefaultChar=*/NULL, /*lpUsedDefaultChar=*/NULL); log_assert(written == (int)out.length()); return out; } static std::string get_last_error_str() { DWORD last_error = GetLastError(); LPWSTR out_w; DWORD size_w = FormatMessageW(/*dwFlags=*/FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS, /*lpSource=*/NULL, /*dwMessageId=*/last_error, /*dwLanguageId=*/0, /*lpBuffer=*/(LPWSTR)&out_w, /*nSize=*/0, /*Arguments=*/NULL); if (size_w == 0) return std::to_string(last_error); std::string out = wstr2str(std::wstring(out_w, size_w)); LocalFree(out_w); return out; } #endif using json11::Json; struct RpcServer { std::string name; RpcServer(const std::string &name) : name(name) { } virtual ~RpcServer() { } virtual void write(const std::string &data) = 0; virtual std::string read() = 0; Json call(const Json &json_request) { std::string request; json_request.dump(request); request += '\n'; log_debug("RPC frontend request: %s", request.c_str()); write(request); std::string response = read(); log_debug("RPC frontend response: %s", response.c_str()); std::string error; Json json_response = Json::parse(response, error); if (json_response.is_null()) log_cmd_error("parsing JSON failed: %s\n", error.c_str()); if (json_response["error"].is_string()) log_cmd_error("RPC frontend returned an error: %s\n", json_response["error"].string_value().c_str()); return json_response; } std::vector get_module_names() { Json response = call(Json::object { { "method", "modules" }, }); bool is_valid = true; std::vector modules; if (response["modules"].is_array()) { for (auto &json_module : response["modules"].array_items()) { if (json_module.is_string()) modules.push_back(json_module.string_value()); else is_valid = false; } } else is_valid = false; if (!is_valid) log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump().c_str()); return modules; } std::pair derive_module(const std::string &module, const dict ¶meters) { Json::object json_parameters; for (auto ¶m : parameters) { std::string type, value; if (param.second.flags & RTLIL::CONST_FLAG_REAL) { type = "real"; value = param.second.decode_string(); } else if (param.second.flags & RTLIL::CONST_FLAG_STRING) { type = "string"; value = param.second.decode_string(); } else if ((param.second.flags & ~RTLIL::CONST_FLAG_SIGNED) == RTLIL::CONST_FLAG_NONE) { type = (param.second.flags & RTLIL::CONST_FLAG_SIGNED) ? "signed" : "unsigned"; value = param.second.as_string(); } else log_cmd_error("Unserializable constant flags 0x%x\n", param.second.flags); json_parameters[param.first.str()] = Json::object { { "type", type }, { "value", value }, }; } Json response = call(Json::object { { "method", "derive" }, { "module", module }, { "parameters", json_parameters }, }); bool is_valid = true; std::string frontend, source; if (response["frontend"].is_string()) frontend = response["frontend"].string_value(); else is_valid = false; if (response["source"].is_string()) source = response["source"].string_value(); else is_valid = false; if (!is_valid) log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump().c_str()); return std::make_pair(frontend, source); } }; struct RpcModule : RTLIL::Module { std::shared_ptr server; RTLIL::IdString derive(RTLIL::Design *design, const dict ¶meters, bool /*mayfail*/) override { std::string stripped_name = name.str(); if (stripped_name.compare(0, 9, "$abstract") == 0) stripped_name = stripped_name.substr(9); log_assert(stripped_name[0] == '\\'); log_header(design, "Executing RPC frontend `%s' for module `%s'.\n", server->name.c_str(), stripped_name.c_str()); std::string parameter_info; for (auto ¶m : parameters) { log("Parameter %s = %s\n", param.first.c_str(), log_signal(RTLIL::SigSpec(param.second))); parameter_info += stringf("%s=%s", param.first.c_str(), log_signal(RTLIL::SigSpec(param.second))); } std::string derived_name; if (parameters.empty()) derived_name = stripped_name; else if (parameter_info.size() > 60) derived_name = "$paramod$" + sha1(parameter_info) + stripped_name; else derived_name = "$paramod" + stripped_name + parameter_info; if (design->has(derived_name)) { log("Found cached RTLIL representation for module `%s'.\n", derived_name.c_str()); } else { std::string command, input; std::tie(command, input) = server->derive_module(stripped_name.substr(1), parameters); std::istringstream input_stream(input); RTLIL::Design *derived_design = new RTLIL::Design; Frontend::frontend_call(derived_design, &input_stream, "" + derived_name.substr(8), command); derived_design->check(); dict name_mangling; bool found_derived_top = false; for (auto module : derived_design->modules()) { std::string original_name = module->name.str(); if (original_name == stripped_name) { found_derived_top = true; name_mangling[original_name] = derived_name; } else { name_mangling[original_name] = derived_name + module->name.str(); } } if (!found_derived_top) log_cmd_error("RPC frontend did not return requested module `%s`!\n", stripped_name.c_str()); for (auto module : derived_design->modules()) for (auto cell : module->cells()) if (name_mangling.count(cell->type.str())) cell->type = name_mangling[cell->type.str()]; for (auto module : derived_design->modules_) { std::string mangled_name = name_mangling[module.first.str()]; log("Importing `%s' as `%s'.\n", log_id(module.first), log_id(mangled_name)); module.second->name = mangled_name; module.second->design = design; module.second->attributes.erase(ID::top); if (!module.second->has_attribute(ID::hdlname)) module.second->set_string_attribute(ID::hdlname, module.first.str()); design->modules_[mangled_name] = module.second; derived_design->modules_.erase(module.first); } delete derived_design; } return derived_name; } RTLIL::Module *clone() const override { RpcModule *new_mod = new RpcModule; new_mod->server = server; cloneInto(new_mod); return new_mod; } }; #if defined(_WIN32) #if defined(_MSC_VER) #include typedef SSIZE_T ssize_t; #endif struct HandleRpcServer : RpcServer { HANDLE hsend, hrecv; HandleRpcServer(const std::string &name, HANDLE hsend, HANDLE hrecv) : RpcServer(name), hsend(hsend), hrecv(hrecv) { } void write(const std::string &data) override { log_assert(data.length() >= 1 && data.find('\n') == data.length() - 1); ssize_t offset = 0; do { DWORD data_written; if (!WriteFile(hsend, &data[offset], data.length() - offset, &data_written, /*lpOverlapped=*/NULL)) log_cmd_error("WriteFile failed: %s\n", get_last_error_str().c_str()); offset += data_written; } while(offset < (ssize_t)data.length()); } std::string read() override { std::string data; ssize_t offset = 0; while (data.length() == 0 || data[data.length() - 1] != '\n') { data.resize(data.length() + 1024); DWORD data_read; if (!ReadFile(hrecv, &data[offset], data.length() - offset, &data_read, /*lpOverlapped=*/NULL)) log_cmd_error("ReadFile failed: %s\n", get_last_error_str().c_str()); offset += data_read; data.resize(offset); size_t term_pos = data.find('\n', offset); if (term_pos != data.length() - 1 && term_pos != std::string::npos) log_cmd_error("read failed: more than one response\n"); } return data; } ~HandleRpcServer() { CloseHandle(hsend); if (hrecv != hsend) CloseHandle(hrecv); } }; #else struct FdRpcServer : RpcServer { int fdsend, fdrecv; pid_t pid; FdRpcServer(const std::string &name, int fdsend, int fdrecv, pid_t pid = -1) : RpcServer(name), fdsend(fdsend), fdrecv(fdrecv), pid(pid) { } void check_pid() { if (pid == -1) return; // If we're communicating with a process, check that it's still running, or we may get killed with SIGPIPE. pid_t wait_result = ::waitpid(pid, NULL, WNOHANG); if (wait_result == -1) log_cmd_error("waitpid failed: %s\n", strerror(errno)); if (wait_result == pid) log_cmd_error("RPC frontend terminated unexpectedly\n"); } void write(const std::string &data) override { log_assert(data.length() >= 1 && data.find('\n') == data.length() - 1); ssize_t offset = 0; do { check_pid(); ssize_t result = ::write(fdsend, &data[offset], data.length() - offset); if (result == -1) log_cmd_error("write failed: %s\n", strerror(errno)); offset += result; } while(offset < (ssize_t)data.length()); } std::string read() override { std::string data; ssize_t offset = 0; while (data.length() == 0 || data[data.length() - 1] != '\n') { data.resize(data.length() + 1024); check_pid(); ssize_t result = ::read(fdrecv, &data[offset], data.length() - offset); if (result == -1) log_cmd_error("read failed: %s\n", strerror(errno)); offset += result; data.resize(offset); size_t term_pos = data.find('\n', offset); if (term_pos != data.length() - 1 && term_pos != std::string::npos) log_cmd_error("read failed: more than one response\n"); } return data; } ~FdRpcServer() { close(fdsend); if (fdrecv != fdsend) close(fdrecv); } }; #endif // RpcFrontend does not inherit from Frontend since it does not read files. struct RpcFrontend : public Pass { RpcFrontend() : Pass("connect_rpc", "connect to RPC frontend") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" connect_rpc -exec [args...]\n"); log(" connect_rpc -path \n"); log("\n"); log("Load modules using an out-of-process frontend.\n"); log("\n"); log(" -exec [args...]\n"); log(" run with arguments [args...]. send requests on stdin, read\n"); log(" responses from stdout.\n"); log("\n"); log(" -path \n"); log(" connect to Unix domain socket at . (Unix)\n"); log(" connect to bidirectional byte-type named pipe at . (Windows)\n"); log("\n"); log("A simple JSON-based, newline-delimited protocol is used for communicating with\n"); log("the frontend. Yosys requests data from the frontend by sending exactly 1 line\n"); log("of JSON. Frontend responds with data or error message by replying with exactly\n"); log("1 line of JSON as well.\n"); log("\n"); log(" -> {\"method\": \"modules\"}\n"); log(" <- {\"modules\": [\"\", ...]}\n"); log(" <- {\"error\": \"\"}\n"); log(" request for the list of modules that can be derived by this frontend.\n"); log(" the 'hierarchy' command will call back into this frontend if a cell\n"); log(" with type is instantiated in the design.\n"); log("\n"); log(" -> {\"method\": \"derive\", \"module\": \", \"parameters\": {\n"); log(" \"\": {\"type\": \"[unsigned|signed|string|real]\",\n"); log(" \"value\": \"\"}, ...}}\n"); log(" <- {\"frontend\": \"[ilang|verilog|...]\",\"source\": \"\"}}\n"); log(" <- {\"error\": \"\"}\n"); log(" request for the module to be derived for a specific set of\n"); log(" parameters. starts with \\ for named parameters, and with $\n"); log(" for unnamed parameters, which are numbered starting at 1.\n"); log(" for integer parameters is always specified as a binary string of unlimited\n"); log(" precision. the returned by the frontend is hygienically parsed\n"); log(" by a built-in Yosys , allowing the RPC frontend to return any\n"); log(" convenient representation of the module. the derived module is cached,\n"); log(" so the response should be the same whenever the same set of parameters\n"); log(" is provided.\n"); } void execute(std::vector args, RTLIL::Design *design) override { log_header(design, "Connecting to RPC frontend.\n"); std::vector command; std::string path; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-exec" && argidx+1 < args.size()) { command.insert(command.begin(), args.begin() + argidx + 1, args.end()); continue; } if (arg == "-path" && argidx+1 < args.size()) { path = args[argidx+1]; continue; } break; } extra_args(args, argidx, design); if ((!command.empty()) + (!path.empty()) != 1) log_cmd_error("Exactly one of -exec, -unix must be specified.\n"); std::shared_ptr server; if (!command.empty()) { std::string command_line; bool first = true; for (auto &arg : command) { if (!first) command_line += ' '; command_line += arg; first = false; } #ifdef _WIN32 std::wstring command_w = str2wstr(command[0]); std::wstring command_path_w; std::wstring command_line_w = str2wstr(command_line); DWORD command_path_len_w; SECURITY_ATTRIBUTES pipe_attr = {}; HANDLE send_r = NULL, send_w = NULL, recv_r = NULL, recv_w = NULL; STARTUPINFOW startup_info = {}; PROCESS_INFORMATION proc_info = {}; command_path_len_w = SearchPathW(/*lpPath=*/NULL, /*lpFileName=*/command_w.c_str(), /*lpExtension=*/L".exe", /*nBufferLength=*/0, /*lpBuffer=*/NULL, /*lpFilePart=*/NULL); if (command_path_len_w == 0) { log_error("SearchPathW failed: %s\n", get_last_error_str().c_str()); goto cleanup_exec; } command_path_w.resize(command_path_len_w - 1); command_path_len_w = SearchPathW(/*lpPath=*/NULL, /*lpFileName=*/command_w.c_str(), /*lpExtension=*/L".exe", /*nBufferLength=*/command_path_len_w, /*lpBuffer=*/&command_path_w[0], /*lpFilePart=*/NULL); log_assert(command_path_len_w == command_path_w.length()); pipe_attr.nLength = sizeof(pipe_attr); pipe_attr.bInheritHandle = TRUE; pipe_attr.lpSecurityDescriptor = NULL; if (!CreatePipe(&send_r, &send_w, &pipe_attr, /*nSize=*/0)) { log_error("CreatePipe failed: %s\n", get_last_error_str().c_str()); goto cleanup_exec; } if (!SetHandleInformation(send_w, HANDLE_FLAG_INHERIT, 0)) { log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str()); goto cleanup_exec; } if (!CreatePipe(&recv_r, &recv_w, &pipe_attr, /*nSize=*/0)) { log_error("CreatePipe failed: %s\n", get_last_error_str().c_str()); goto cleanup_exec; } if (!SetHandleInformation(recv_r, HANDLE_FLAG_INHERIT, 0)) { log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str()); goto cleanup_exec; } startup_info.cb = sizeof(startup_info); startup_info.hStdInput = send_r; startup_info.hStdOutput = recv_w; startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); startup_info.dwFlags |= STARTF_USESTDHANDLES; if (!CreateProcessW(/*lpApplicationName=*/command_path_w.c_str(), /*lpCommandLine=*/&command_line_w[0], /*lpProcessAttributes=*/NULL, /*lpThreadAttributes=*/NULL, /*bInheritHandles=*/TRUE, /*dwCreationFlags=*/0, /*lpEnvironment=*/NULL, /*lpCurrentDirectory=*/NULL, &startup_info, &proc_info)) { log_error("CreateProcessW failed: %s\n", get_last_error_str().c_str()); goto cleanup_exec; } CloseHandle(proc_info.hProcess); CloseHandle(proc_info.hThread); server = std::make_shared(path, send_w, recv_r); send_w = NULL; recv_r = NULL; cleanup_exec: if (send_r != NULL) CloseHandle(send_r); if (send_w != NULL) CloseHandle(send_w); if (recv_r != NULL) CloseHandle(recv_r); if (recv_w != NULL) CloseHandle(recv_w); #else std::vector argv; int send[2] = {-1,-1}, recv[2] = {-1,-1}; posix_spawn_file_actions_t file_actions, *file_actions_p = NULL; pid_t pid; for (auto &arg : command) argv.push_back(&arg[0]); argv.push_back(nullptr); if (pipe(send) != 0) { log_error("pipe failed: %s\n", strerror(errno)); goto cleanup_exec; } if (pipe(recv) != 0) { log_error("pipe failed: %s\n", strerror(errno)); goto cleanup_exec; } if (posix_spawn_file_actions_init(&file_actions) != 0) { log_error("posix_spawn_file_actions_init failed: %s\n", strerror(errno)); goto cleanup_exec; } file_actions_p = &file_actions; if (posix_spawn_file_actions_adddup2(file_actions_p, send[0], STDIN_FILENO) != 0) { log_error("posix_spawn_file_actions_adddup2 failed: %s\n", strerror(errno)); goto cleanup_exec; } if (posix_spawn_file_actions_addclose(file_actions_p, send[1]) != 0) { log_error("posix_spawn_file_actions_addclose failed: %s\n", strerror(errno)); goto cleanup_exec; } if (posix_spawn_file_actions_adddup2(file_actions_p, recv[1], STDOUT_FILENO) != 0) { log_error("posix_spawn_file_actions_adddup2 failed: %s\n", strerror(errno)); goto cleanup_exec; } if (posix_spawn_file_actions_addclose(file_actions_p, recv[0]) != 0) { log_error("posix_spawn_file_actions_addclose failed: %s\n", strerror(errno)); goto cleanup_exec; } if (posix_spawnp(&pid, argv[0], file_actions_p, /*attrp=*/NULL, argv.data(), environ) != 0) { log_error("posix_spawnp failed: %s\n", strerror(errno)); goto cleanup_exec; } server = std::make_shared(command_line, send[1], recv[0], pid); send[1] = -1; recv[0] = -1; cleanup_exec: if (send[0] != -1) close(send[0]); if (send[1] != -1) close(send[1]); if (recv[0] != -1) close(recv[0]); if (recv[1] != -1) close(recv[1]); if (file_actions_p != NULL) posix_spawn_file_actions_destroy(file_actions_p); #endif } else if (!path.empty()) { #ifdef _WIN32 std::wstring path_w = str2wstr(path); HANDLE h; h = CreateFileW(path_w.c_str(), GENERIC_READ|GENERIC_WRITE, /*dwShareMode=*/0, /*lpSecurityAttributes=*/NULL, /*dwCreationDisposition=*/OPEN_EXISTING, /*dwFlagsAndAttributes=*/0, /*hTemplateFile=*/NULL); if (h == INVALID_HANDLE_VALUE) { log_error("CreateFileW failed: %s\n", get_last_error_str().c_str()); goto cleanup_path; } server = std::make_shared(path, h, h); cleanup_path: ; #else struct sockaddr_un addr; addr.sun_family = AF_UNIX; strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1); int fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) { log_error("socket failed: %s\n", strerror(errno)); goto cleanup_path; } if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { log_error("connect failed: %s\n", strerror(errno)); goto cleanup_path; } server = std::make_shared(path, fd, fd); fd = -1; cleanup_path: if (fd != -1) close(fd); #endif } if (!server) log_cmd_error("Failed to connect to RPC frontend.\n"); for (auto &module_name : server->get_module_names()) { log("Linking module `%s'.\n", module_name.c_str()); RpcModule *module = new RpcModule; module->name = "$abstract\\" + module_name; module->server = server; design->add(module); } } } RpcFrontend; YOSYS_NAMESPACE_END } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/* gzwrite.c -- zlib functions for writing gzip files
 * Copyright (C) 2004, 2005, 2010 Mark Adler
 * For conditions of distribution and use, see copyright notice in zlib.h
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "misc/util/abc_global.h"

#include "gzguts.h"

ABC_NAMESPACE_IMPL_START

/* Local functions */
local int gz_init OF((gz_statep));
local int gz_comp OF((gz_statep, int));
local int gz_zero OF((gz_statep, z_off64_t));

/* Initialize state for writing a gzip file.  Mark initialization by setting
   state->size to non-zero.  Return -1 on failure or 0 on success. */
local int gz_init(gz_statep state)
{
    int ret;
    z_streamp strm = &(state->strm);

    /* allocate input and output buffers */
    state->in = (unsigned char *)malloc(state->want);
    state->out = (unsigned char *)malloc(state->want);
    if (state->in == NULL || state->out == NULL) {
        if (state->out != NULL)
            free(state->out);
        if (state->in != NULL)
            free(state->in);
        gz_error(state, Z_MEM_ERROR, "out of memory");
        return -1;
    }

    /* allocate deflate memory, set up for gzip compression */
    strm->zalloc = Z_NULL;
    strm->zfree = Z_NULL;
    strm->opaque = Z_NULL;
    ret = deflateInit2(strm, state->level, Z_DEFLATED,
                       15 + 16, 8, state->strategy);
    if (ret != Z_OK) {
        free(state->in);
        gz_error(state, Z_MEM_ERROR, "out of memory");
        return -1;
    }

    /* mark state as initialized */
    state->size = state->want;

    /* initialize write buffer */
    strm->avail_out = state->size;
    strm->next_out = state->out;
    state->next = strm->next_out;
    return 0;
}

/* Compress whatever is at avail_in and next_in and write to the output file.
   Return -1 if there is an error writing to the output file, otherwise 0.
   flush is assumed to be a valid deflate() flush value.  If flush is Z_FINISH,
   then the deflate() state is reset to start a new gzip stream. */
local int gz_comp(gz_statep state, int flush)
{
    int ret, got;
    unsigned have;
    z_streamp strm = &(state->strm);

    /* allocate memory if this is the first time through */
    if (state->size == 0 && gz_init(state) == -1)
        return -1;

    /* run deflate() on provided input until it produces no more output */
    ret = Z_OK;
    do {
        /* write out current buffer contents if full, or if flushing, but if
           doing Z_FINISH then don't write until we get to Z_STREAM_END */
        if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&
            (flush != Z_FINISH || ret == Z_STREAM_END))) {
            have = (unsigned)(strm->next_out - state->next);
            if (have && ((got = write(state->fd, state->next, have)) < 0 ||
                         (unsigned)got != have)) {
                gz_error(state, Z_ERRNO, zstrerror());
                return -1;
            }
            if (strm->avail_out == 0) {
                strm->avail_out = state->size;
                strm->next_out = state->out;
            }
            state->next = strm->next_out;
        }

        /* compress */
        have = strm->avail_out;
        ret = deflate(strm, flush);
        if (ret == Z_STREAM_ERROR) {
            gz_error(state, Z_STREAM_ERROR,
                      "internal error: deflate stream corrupt");
            return -1;
        }
        have -= strm->avail_out;
    } while (have);

    /* if that completed a deflate stream, allow another to start */
    if (flush == Z_FINISH)
        deflateReset(strm);

    /* all done, no errors */
    return 0;
}

/* Compress len zeros to output.  Return -1 on error, 0 on success. */
local int gz_zero(gz_statep state, z_off64_t len)
{
    int first;
    unsigned n;
    z_streamp strm = &(state->strm);

    /* consume whatever's left in the input buffer */
    if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
        return -1;

    /* compress len zeros (len guaranteed > 0) */
    first = 1;
    while (len) {
        n = GT_OFF(state->size) || (z_off64_t)state->size > len ?
            (unsigned)len : state->size;
        if (first) {
            memset(state->in, 0, (size_t)n);
            first = 0;
        }
        strm->avail_in = n;
        strm->next_in = state->in;
        state->pos += n;
        if (gz_comp(state, Z_NO_FLUSH) == -1)
            return -1;
        len -= n;
    }
    return 0;
}

/* -- see zlib.h -- */
int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len)
{
    unsigned put = len;
    unsigned n;
    gz_statep state;
    z_streamp strm;

    /* get internal structure */
    if (file == NULL)
        return 0;
    state = (gz_statep)file;
    strm = &(state->strm);

    /* check that we're writing and that there's no error */
    if (state->mode != GZ_WRITE || state->err != Z_OK)
        return 0;

    /* since an int is returned, make sure len fits in one, otherwise return
       with an error (this avoids the flaw in the interface) */
    if ((int)len < 0) {
        gz_error(state, Z_BUF_ERROR, "requested length does not fit in int");
        return 0;
    }

    /* if len is zero, avoid unnecessary operations */
    if (len == 0)
        return 0;

    /* allocate memory if this is the first time through */
    if (state->size == 0 && gz_init(state) == -1)
        return 0;

    /* check for seek request */
    if (state->seek) {
        state->seek = 0;
        if (gz_zero(state, state->skip) == -1)
            return 0;
    }

    /* for small len, copy to input buffer, otherwise compress directly */
    if (len < state->size) {
        /* copy to input buffer, compress when full */
        do {
            if (strm->avail_in == 0)
                strm->next_in = state->in;
            n = state->size - strm->avail_in;
            if (n > len)
                n = len;
            memcpy(strm->next_in + strm->avail_in, buf, n);
            strm->avail_in += n;
            state->pos += n;
            buf = (char *)buf + n;
            len -= n;
            if (len && gz_comp(state, Z_NO_FLUSH) == -1)
                return 0;
        } while (len);
    }
    else {
        /* consume whatever's left in the input buffer */
        if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
            return 0;

        /* directly compress user buffer to file */
        strm->avail_in = len;
        strm->next_in = (unsigned char *)(voidp)buf;
        state->pos += len;
        if (gz_comp(state, Z_NO_FLUSH) == -1)
            return 0;
    }

    /* input was all buffered or compressed (put will fit in int) */
    return (int)put;
}

/* -- see zlib.h -- */
int ZEXPORT gzputc(gzFile file, int c)
{
    unsigned char buf[1];
    gz_statep state;
    z_streamp strm;

    /* get internal structure */
    if (file == NULL)
        return -1;
    state = (gz_statep)file;
    strm = &(state->strm);

    /* check that we're writing and that there's no error */
    if (state->mode != GZ_WRITE || state->err != Z_OK)
        return -1;

    /* check for seek request */
    if (state->seek) {
        state->seek = 0;
        if (gz_zero(state, state->skip) == -1)
            return -1;
    }

    /* try writing to input buffer for speed (state->size == 0 if buffer not
       initialized) */
    if (strm->avail_in < state->size) {
        if (strm->avail_in == 0)
            strm->next_in = state->in;
        strm->next_in[strm->avail_in++] = c;
        state->pos++;
        return c;
    }

    /* no room in buffer or not initialized, use gz_write() */
    buf[0] = c;
    if (gzwrite(file, buf, 1) != 1)
        return -1;
    return c;
}

/* -- see zlib.h -- */
int ZEXPORT gzputs(gzFile file, const char *str)
{
    int ret;
    unsigned len;

    /* write string */
    len = (unsigned)strlen(str);
    ret = gzwrite(file, str, len);
    return ret == 0 && len != 0 ? -1 : ret;
}

#ifdef STDC
#include <stdarg.h>

/* -- see zlib.h -- */
int ZEXPORTVA gzprintf (gzFile file, const char *format, ...)
{
    int size, len;
    gz_statep state;
    z_streamp strm;
    va_list va;

    /* get internal structure */
    if (file == NULL)
        return -1;
    state = (gz_statep)file;
    strm = &(state->strm);

    /* check that we're writing and that there's no error */
    if (state->mode != GZ_WRITE || state->err != Z_OK)
        return 0;

    /* make sure we have some buffer space */
    if (state->size == 0 && gz_init(state) == -1)
        return 0;

    /* check for seek request */
    if (state->seek) {
        state->seek = 0;
        if (gz_zero(state, state->skip) == -1)
            return 0;
    }

    /* consume whatever's left in the input buffer */
    if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
        return 0;

    /* do the printf() into the input buffer, put length in len */
    size = (int)(state->size);
    state->in[size - 1] = 0;
    va_start(va, format);
#ifdef NO_vsnprintf
#  ifdef HAS_vsprintf_void
    (void)vsprintf(state->in, format, va);
    va_end(va);
    for (len = 0; len < size; len++)
        if (state->in[len] == 0) break;
#  else
    len = vsprintf(state->in, format, va);
    va_end(va);
#  endif
#else
#  ifdef HAS_vsnprintf_void
    (void)vsnprintf(state->in, size, format, va);
    va_end(va);
    len = strlen(state->in);
#  else
    len = vsnprintf((char *)(state->in), size, format, va);
    va_end(va);
#  endif
#endif

    /* check that printf() results fit in buffer */
    if (len <= 0 || len >= (int)size || state->in[size - 1] != 0)
        return 0;

    /* update buffer and position, defer compression until needed */
    strm->avail_in = (unsigned)len;
    strm->next_in = state->in;
    state->pos += len;
    return len;
}

#else /* !STDC */

/* -- see zlib.h -- */
int ZEXPORTVA gzprintf (gzFile file, const char *format, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10,
                       int a11, int a12, int a13, int a14, int a15, int a16, int a17, int a18, int a19, int a20)
{
    int size, len;
    gz_statep state;
    z_streamp strm;

    /* get internal structure */
    if (file == NULL)
        return -1;
    state = (gz_statep)file;
    strm = &(state->strm);

    /* check that we're writing and that there's no error */
    if (state->mode != GZ_WRITE || state->err != Z_OK)
        return 0;

    /* make sure we have some buffer space */
    if (state->size == 0 && gz_init(state) == -1)
        return 0;

    /* check for seek request */
    if (state->seek) {
        state->seek = 0;
        if (gz_zero(state, state->skip) == -1)
            return 0;
    }

    /* consume whatever's left in the input buffer */
    if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
        return 0;

    /* do the printf() into the input buffer, put length in len */
    size = (int)(state->size);
    state->in[size - 1] = 0;
#ifdef NO_snprintf
#  ifdef HAS_sprintf_void
    sprintf(state->in, format, a1, a2, a3, a4, a5, a6, a7, a8,
            a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
    for (len = 0; len < size; len++)
        if (state->in[len] == 0) break;
#  else
    len = sprintf(state->in, format, a1, a2, a3, a4, a5, a6, a7, a8,
                a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
#  endif
#else
#  ifdef HAS_snprintf_void
    snprintf(state->in, size, format, a1, a2, a3, a4, a5, a6, a7, a8,
             a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
    len = strlen(state->in);
#  else
    len = snprintf(state->in, size, format, a1, a2, a3, a4, a5, a6, a7, a8,
                 a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
#  endif
#endif

    /* check that printf() results fit in buffer */
    if (len <= 0 || len >= (int)size || state->in[size - 1] != 0)
        return 0;

    /* update buffer and position, defer compression until needed */
    strm->avail_in = (unsigned)len;
    strm->next_in = state->in;
    state->pos += len;
    return len;
}

#endif

/* -- see zlib.h -- */
int ZEXPORT gzflush(gzFile file, int flush)
{
    gz_statep state;

    /* get internal structure */
    if (file == NULL)
        return -1;
    state = (gz_statep)file;

    /* check that we're writing and that there's no error */
    if (state->mode != GZ_WRITE || state->err != Z_OK)
        return Z_STREAM_ERROR;

    /* check flush parameter */
    if (flush < 0 || flush > Z_FINISH)
        return Z_STREAM_ERROR;

    /* check for seek request */
    if (state->seek) {
        state->seek = 0;
        if (gz_zero(state, state->skip) == -1)
            return -1;
    }

    /* compress remaining data with requested flush */
    gz_comp(state, flush);
    return state->err;
}

/* -- see zlib.h -- */
int ZEXPORT gzsetparams(gzFile file, int level, int strategy)
{
    gz_statep state;
    z_streamp strm;

    /* get internal structure */
    if (file == NULL)
        return Z_STREAM_ERROR;
    state = (gz_statep)file;
    strm = &(state->strm);

    /* check that we're writing and that there's no error */
    if (state->mode != GZ_WRITE || state->err != Z_OK)
        return Z_STREAM_ERROR;

    /* if no change is requested, then do nothing */
    if (level == state->level && strategy == state->strategy)
        return Z_OK;

    /* check for seek request */
    if (state->seek) {
        state->seek = 0;
        if (gz_zero(state, state->skip) == -1)
            return -1;
    }

    /* change compression parameters for subsequent input */
    if (state->size) {
        /* flush previous input with previous parameters before changing */
        if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1)
            return state->err;
        deflateParams(strm, level, strategy);
    }
    state->level = level;
    state->strategy = strategy;
    return Z_OK;
}

/* -- see zlib.h -- */
int ZEXPORT gzclose_w(gzFile file)
{
    int ret = 0;
    gz_statep state;

    /* get internal structure */
    if (file == NULL)
        return Z_STREAM_ERROR;
    state = (gz_statep)file;

    /* check that we're writing */
    if (state->mode != GZ_WRITE)
        return Z_STREAM_ERROR;

    /* check for seek request */
    if (state->seek) {
        state->seek = 0;
        ret += gz_zero(state, state->skip);
    }

    /* flush, free memory, and close file */
    ret += gz_comp(state, Z_FINISH);
    (void)deflateEnd(&(state->strm));
    free(state->out);
    free(state->in);
    gz_error(state, Z_OK, NULL);
    free(state->path);
    ret += close(state->fd);
    free(state);
    return ret ? Z_ERRNO : Z_OK;
}


ABC_NAMESPACE_IMPL_END