Revert "Move BinPAC, bifcl, af_packet, and gen_zam submodules into main zeek repo"

This commit is contained in:
Tim Wojtulewicz 2025-08-15 15:11:22 -07:00 committed by GitHub
parent a10a70994e
commit e64ec54172
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
146 changed files with 50 additions and 20635 deletions

12
.gitmodules vendored
View file

@ -1,6 +1,9 @@
[submodule "auxil/zeek-aux"]
path = auxil/zeek-aux
url = https://github.com/zeek/zeek-aux
[submodule "auxil/binpac"]
path = auxil/binpac
url = https://github.com/zeek/binpac
[submodule "auxil/zeekctl"]
path = auxil/zeekctl
url = https://github.com/zeek/zeekctl
@ -19,6 +22,9 @@
[submodule "auxil/netcontrol-connectors"]
path = auxil/netcontrol-connectors
url = https://github.com/zeek/zeek-netcontrol
[submodule "auxil/bifcl"]
path = auxil/bifcl
url = https://github.com/zeek/bifcl
[submodule "doc"]
path = doc
url = https://github.com/zeek/zeek-docs
@ -40,6 +46,9 @@
[submodule "auxil/zeek-client"]
path = auxil/zeek-client
url = https://github.com/zeek/zeek-client
[submodule "auxil/gen-zam"]
path = auxil/gen-zam
url = https://github.com/zeek/gen-zam
[submodule "auxil/c-ares"]
path = auxil/c-ares
url = https://github.com/c-ares/c-ares
@ -49,6 +58,9 @@
[submodule "auxil/spicy"]
path = auxil/spicy
url = https://github.com/zeek/spicy
[submodule "auxil/zeek-af_packet-plugin"]
path = auxil/zeek-af_packet-plugin
url = https://github.com/zeek/zeek-af_packet-plugin.git
[submodule "auxil/libunistd"]
path = auxil/libunistd
url = https://github.com/zeek/libunistd

30
CHANGES
View file

@ -1,33 +1,3 @@
8.1.0-dev.66 | 2025-08-15 14:02:08 -0700
* Fix some clang-tidy findings in generated BIF code (Tim Wojtulewicz, Corelight)
* Fix clang-tidy and pre-commit warnings for gen-zam code files (Tim Wojtulewicz, Corelight)
* Move gen-zam code into the main Zeek repository (Tim Wojtulewicz, Corelight)
This is based on commit 56a6db00b887c79d26f303676677cb490d1c296d from
the gen-zam repository.
* Move zeek-af_packet-plugin code into the main Zeek repository (Tim Wojtulewicz, Corelight)
This is based on commit b89a6f64123f778090d1dd6ec48e6b8e8906ea11 from
the zeek-af_packet-plugin repository.
* Move the bifcl code into the main Zeek repository (Tim Wojtulewicz, Corelight)
This is based on commit 5947749f7850b075f11d6a2aaefe7dad4f63cb62f from
the bifcl repository.
* Fix clang-tidy findings in the binpac lib code (Tim Wojtulewicz, Corelight)
* Add copyright headers to all of the binpac source files (Tim Wojtulewicz, Corelight)
* Move binpac code into the main Zeek repository (Tim Wojtulewicz, Corelight)
This is based on commit 48f75b5f6415fe9d597e3e991cec635b1bc400dc from
the binpac repository.
8.1.0-dev.56 | 2025-08-13 21:20:50 +0200
* ci: Run zeekctl and builtin tasks with Debian 13, too (Arne Welzel, Corelight)

View file

@ -396,14 +396,14 @@ endfunction ()
add_zeek_dynamic_plugin_build_interface_include_directories(
${PROJECT_SOURCE_DIR}/src/include
${PROJECT_SOURCE_DIR}/tools/binpac/lib
${PROJECT_SOURCE_DIR}/auxil/binpac/lib
${PROJECT_SOURCE_DIR}/auxil/broker/libbroker
${PROJECT_SOURCE_DIR}/auxil/paraglob/include
${PROJECT_SOURCE_DIR}/auxil/prometheus-cpp/core/include
${PROJECT_SOURCE_DIR}/auxil/expected-lite/include
${CMAKE_BINARY_DIR}/src
${CMAKE_BINARY_DIR}/src/include
${CMAKE_BINARY_DIR}/tools/binpac/lib
${CMAKE_BINARY_DIR}/auxil/binpac/lib
${CMAKE_BINARY_DIR}/auxil/broker/libbroker
${CMAKE_BINARY_DIR}/auxil/prometheus-cpp/core/include)
@ -892,27 +892,27 @@ if (BUILD_STATIC_BINPAC)
set(ENABLE_STATIC_ONLY true)
endif ()
add_subdirectory(tools/binpac)
add_subdirectory(auxil/binpac)
set(ENABLE_STATIC_ONLY ${ENABLE_STATIC_ONLY_SAVED})
# FIXME: avoid hard-coding a path for multi-config generator support. See the
# TODO in ZeekPluginConfig.cmake.in.
set(BINPAC_EXE_PATH "${CMAKE_BINARY_DIR}/tools/binpac/src/binpac${CMAKE_EXECUTABLE_SUFFIX}")
set(BINPAC_EXE_PATH "${CMAKE_BINARY_DIR}/auxil/binpac/src/binpac${CMAKE_EXECUTABLE_SUFFIX}")
set(_binpac_exe_path "included")
# Need to call find_package so it sets up the include paths used by plugin builds.
find_package(BinPAC REQUIRED)
add_executable(Zeek::BinPAC ALIAS binpac)
add_subdirectory(tools/bifcl)
add_subdirectory(auxil/bifcl)
add_executable(Zeek::BifCl ALIAS bifcl)
# FIXME: avoid hard-coding a path for multi-config generator support. See the
# TODO in ZeekPluginConfig.cmake.in.
set(BIFCL_EXE_PATH "${CMAKE_BINARY_DIR}/tools/bifcl/bifcl${CMAKE_EXECUTABLE_SUFFIX}")
set(BIFCL_EXE_PATH "${CMAKE_BINARY_DIR}/auxil/bifcl/bifcl${CMAKE_EXECUTABLE_SUFFIX}")
set(_bifcl_exe_path "included")
if (NOT GEN_ZAM_EXE_PATH)
add_subdirectory(tools/gen-zam)
add_subdirectory(auxil/gen-zam)
endif ()
if (ENABLE_JEMALLOC)
@ -1189,6 +1189,18 @@ endif ()
# Tell the plugin code that we're building as part of the main tree.
set(ZEEK_PLUGIN_INTERNAL_BUILD true CACHE INTERNAL "" FORCE)
set(ZEEK_HAVE_AF_PACKET no)
if (${CMAKE_SYSTEM_NAME} MATCHES Linux)
if (NOT DISABLE_AF_PACKET)
if (NOT AF_PACKET_PLUGIN_PATH)
set(AF_PACKET_PLUGIN_PATH ${CMAKE_SOURCE_DIR}/auxil/zeek-af_packet-plugin)
endif ()
list(APPEND ZEEK_INCLUDE_PLUGINS ${AF_PACKET_PLUGIN_PATH})
set(ZEEK_HAVE_AF_PACKET yes)
endif ()
endif ()
set(ZEEK_HAVE_JAVASCRIPT no)
if (NOT DISABLE_JAVASCRIPT)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/auxil/zeekjs/cmake)
@ -1208,7 +1220,6 @@ if (NOT DISABLE_JAVASCRIPT)
endif ()
endif ()
set(ZEEK_HAVE_AF_PACKET no CACHE INTERNAL "Zeek has AF_PACKET support")
set(ZEEK_HAVE_JAVASCRIPT ${ZEEK_HAVE_JAVASCRIPT} CACHE INTERNAL "Zeek has JavaScript support")
set(DEFAULT_ZEEKPATH_PATHS

View file

@ -1 +1 @@
8.1.0-dev.66
8.1.0-dev.56

1
auxil/bifcl Submodule

@ -0,0 +1 @@
Subproject commit 5947749f7850b075f11d6a2aaefe7dad4f63cb62

1
auxil/binpac Submodule

@ -0,0 +1 @@
Subproject commit 48f75b5f6415fe9d597e3e991cec635b1bc400dc

1
auxil/gen-zam Submodule

@ -0,0 +1 @@
Subproject commit 56a6db00b887c79d26f303676677cb490d1c296d

@ -0,0 +1 @@
Subproject commit b89a6f64123f778090d1dd6ec48e6b8e8906ea11

View file

@ -5691,31 +5691,6 @@ export {
};
}
module AF_Packet;
export {
## Size of the ring-buffer.
const buffer_size = 128 * 1024 * 1024 &redef;
## Size of an individual block. Needs to be a multiple of page size.
const block_size = 4096 * 8 &redef;
## Retire timeout for a single block.
const block_timeout = 10msec &redef;
## Toggle whether to use hardware timestamps.
const enable_hw_timestamping = F &redef;
## Toggle whether to use PACKET_FANOUT.
const enable_fanout = T &redef;
## Toggle defragmentation of IP packets using PACKET_FANOUT_FLAG_DEFRAG.
const enable_defrag = F &redef;
## Fanout mode.
const fanout_mode = FANOUT_HASH &redef;
## Fanout ID.
const fanout_id = 23 &redef;
## Link type (default Ethernet).
const link_type = 1 &redef;
## Checksum validation mode.
const checksum_validation_mode: ChecksumMode = CHECKSUM_ON &redef;
}
module DCE_RPC;
export {

View file

@ -13,4 +13,3 @@ zeek_add_subdir_library(
PktSrc.cc)
add_subdirectory(pcap)
add_subdirectory(af_packet)

View file

@ -1,326 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "zeek/iosource/af_packet/AF_Packet.h"
#include "zeek/iosource/af_packet/RX_Ring.h"
#include "zeek/iosource/af_packet/af_packet.bif.h"
#ifndef TP_STATUS_CSUM_VALID
#define TP_STATUS_CSUM_VALID (1 << 7)
#endif
using namespace zeek::iosource::pktsrc;
AF_PacketSource::~AF_PacketSource() { Close(); }
AF_PacketSource::AF_PacketSource(const std::string& path, bool is_live) {
if ( ! is_live )
Error("AF_Packet source does not support offline input");
current_filter = -1;
props.path = path;
props.is_live = is_live;
socket_fd = -1;
rx_ring = nullptr;
checksum_mode = zeek::BifConst::AF_Packet::checksum_validation_mode->AsEnum();
}
void AF_PacketSource::Open() {
uint64_t buffer_size = zeek::BifConst::AF_Packet::buffer_size;
uint64_t block_size = zeek::BifConst::AF_Packet::block_size;
int block_timeout_msec = static_cast<int>(zeek::BifConst::AF_Packet::block_timeout * 1000.0);
int link_type = zeek::BifConst::AF_Packet::link_type;
bool enable_hw_timestamping = zeek::BifConst::AF_Packet::enable_hw_timestamping;
bool enable_fanout = zeek::BifConst::AF_Packet::enable_fanout;
bool enable_defrag = zeek::BifConst::AF_Packet::enable_defrag;
socket_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if ( socket_fd < 0 ) {
Error(errno ? strerror(errno) : "unable to create socket");
return;
}
auto info = GetInterfaceInfo(props.path);
if ( ! info.Valid() ) {
Error(errno ? strerror(errno) : "unable to get interface information");
close(socket_fd);
socket_fd = -1;
return;
}
if ( ! info.IsUp() ) {
Error("interface is down");
close(socket_fd);
socket_fd = -1;
return;
}
// Create RX-ring
try {
rx_ring = new RX_Ring(socket_fd, buffer_size, block_size, block_timeout_msec);
} catch ( RX_RingException& e ) {
Error(errno ? strerror(errno) : "unable to create RX-ring");
close(socket_fd);
return;
}
// Setup interface
if ( ! BindInterface(info) ) {
Error(errno ? strerror(errno) : "unable to bind to interface");
close(socket_fd);
return;
}
if ( ! EnablePromiscMode(info) ) {
Error(errno ? strerror(errno) : "unable enter promiscuous mode");
close(socket_fd);
return;
}
if ( ! ConfigureFanoutGroup(enable_fanout, enable_defrag) ) {
Error(errno ? strerror(errno) : "failed to join fanout group");
close(socket_fd);
return;
}
if ( ! ConfigureHWTimestamping(enable_hw_timestamping) ) {
Error(errno ? strerror(errno) : "failed to configure hardware timestamping");
close(socket_fd);
return;
}
props.netmask = NETMASK_UNKNOWN;
props.selectable_fd = socket_fd;
props.is_live = true;
props.link_type = link_type;
stats.received = stats.dropped = stats.link = stats.bytes_received = 0;
num_discarded = 0;
Opened(props);
}
AF_PacketSource::InterfaceInfo AF_PacketSource::GetInterfaceInfo(const std::string& path) {
AF_PacketSource::InterfaceInfo info;
struct ifreq ifr;
int ret;
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", path.c_str());
ret = ioctl(socket_fd, SIOCGIFFLAGS, &ifr);
if ( ret < 0 )
return info;
info.flags = ifr.ifr_flags;
ret = ioctl(socket_fd, SIOCGIFINDEX, &ifr);
if ( ret < 0 )
return info;
info.index = ifr.ifr_ifindex;
return info;
}
bool AF_PacketSource::BindInterface(const AF_PacketSource::InterfaceInfo& info) {
struct sockaddr_ll saddr_ll;
int ret;
memset(&saddr_ll, 0, sizeof(saddr_ll));
saddr_ll.sll_family = AF_PACKET;
saddr_ll.sll_protocol = htons(ETH_P_ALL);
saddr_ll.sll_ifindex = info.index;
ret = bind(socket_fd, (struct sockaddr*)&saddr_ll, sizeof(saddr_ll));
return (ret >= 0);
}
bool AF_PacketSource::EnablePromiscMode(const AF_PacketSource::InterfaceInfo& info) {
struct packet_mreq mreq;
int ret;
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = info.index;
mreq.mr_type = PACKET_MR_PROMISC;
ret = setsockopt(socket_fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
return (ret >= 0);
}
bool AF_PacketSource::ConfigureFanoutGroup(bool enabled, bool defrag) {
if ( enabled ) {
uint32_t fanout_id = zeek::BifConst::AF_Packet::fanout_id;
uint32_t fanout_arg = ((fanout_id & 0xffff) | (GetFanoutMode(defrag) << 16));
if ( setsockopt(socket_fd, SOL_PACKET, PACKET_FANOUT, &fanout_arg, sizeof(fanout_arg)) < 0 )
return false;
}
return true;
}
bool AF_PacketSource::ConfigureHWTimestamping(bool enabled) {
if ( enabled ) {
struct ifreq ifr;
struct hwtstamp_config hwts_cfg;
memset(&hwts_cfg, 0, sizeof(hwts_cfg));
hwts_cfg.tx_type = HWTSTAMP_TX_OFF;
hwts_cfg.rx_filter = HWTSTAMP_FILTER_ALL;
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", props.path.c_str());
ifr.ifr_data = &hwts_cfg;
if ( ioctl(socket_fd, SIOCSHWTSTAMP, &ifr) < 0 )
return false;
int opt = SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE;
if ( setsockopt(socket_fd, SOL_PACKET, PACKET_TIMESTAMP, &opt, sizeof(opt)) < 0 )
return false;
}
return true;
}
uint32_t AF_PacketSource::GetFanoutMode(bool defrag) {
uint32_t fanout_mode;
switch ( zeek::BifConst::AF_Packet::fanout_mode->AsEnum() ) {
case BifEnum::AF_Packet::FANOUT_CPU: fanout_mode = PACKET_FANOUT_CPU; break;
#ifdef PACKET_FANOUT_QM
case BifEnum::AF_Packet::FANOUT_QM: fanout_mode = PACKET_FANOUT_QM; break;
#endif
#ifdef PACKET_FANOUT_CBPF
case BifEnum::AF_Packet::FANOUT_CBPF: fanout_mode = PACKET_FANOUT_CBPF; break;
#endif
#ifdef PACKET_FANOUT_EBPF
case BifEnum::AF_Packet::FANOUT_EBPF: fanout_mode = PACKET_FANOUT_EBPF; break;
#endif
default: fanout_mode = PACKET_FANOUT_HASH; break;
}
if ( defrag )
fanout_mode |= PACKET_FANOUT_FLAG_DEFRAG;
return fanout_mode;
}
void AF_PacketSource::Close() {
if ( socket_fd < 0 )
return;
delete rx_ring;
rx_ring = nullptr;
close(socket_fd);
socket_fd = -1;
Closed();
}
bool AF_PacketSource::ExtractNextPacket(zeek::Packet* pkt) {
if ( ! socket_fd )
return false;
struct tpacket3_hdr* packet = nullptr;
const u_char* data;
while ( true ) {
if ( ! rx_ring->GetNextPacket(&packet) )
return false;
current_hdr.ts.tv_sec = packet->tp_sec;
current_hdr.ts.tv_usec = packet->tp_nsec / 1000;
current_hdr.caplen = packet->tp_snaplen;
current_hdr.len = packet->tp_len;
data = (u_char*)packet + packet->tp_mac;
if ( ! ApplyBPFFilter(current_filter, &current_hdr, data) ) {
++num_discarded;
DoneWithPacket();
continue;
}
pkt->Init(props.link_type, &current_hdr.ts, current_hdr.caplen, current_hdr.len, data);
if ( packet->tp_status & TP_STATUS_VLAN_VALID )
pkt->vlan = packet->hv1.tp_vlan_tci & 0x0fff;
switch ( checksum_mode ) {
case BifEnum::AF_Packet::CHECKSUM_OFF: {
// If set to off, just accept whatever checksum in the packet is correct and
// skip checking it here and in Zeek.
pkt->l4_checksummed = true;
break;
}
case BifEnum::AF_Packet::CHECKSUM_KERNEL: {
// If set to kernel, check whether the kernel thinks the checksum is valid. If it
// does, tell Zeek to skip checking by itself.
if ( ((packet->tp_status & TP_STATUS_CSUM_VALID) != 0) ||
((packet->tp_status & TP_STATUS_CSUMNOTREADY) != 0) )
pkt->l4_checksummed = true;
else
pkt->l4_checksummed = false;
break;
}
case BifEnum::AF_Packet::CHECKSUM_ON:
default: {
// Let Zeek handle it.
pkt->l4_checksummed = false;
break;
}
}
if ( current_hdr.len == 0 || current_hdr.caplen == 0 ) {
Weird("empty_af_packet_header", pkt);
return false;
}
stats.received++;
stats.bytes_received += current_hdr.len;
return true;
}
return false;
}
void AF_PacketSource::DoneWithPacket() { rx_ring->ReleasePacket(); }
bool AF_PacketSource::PrecompileFilter(int index, const std::string& filter) {
return PktSrc::PrecompileBPFFilter(index, filter);
}
bool AF_PacketSource::SetFilter(int index) {
current_filter = index;
return true;
}
void AF_PacketSource::Statistics(Stats* s) {
if ( ! socket_fd ) {
s->received = s->bytes_received = s->link = s->dropped = 0;
return;
}
struct tpacket_stats_v3 tp_stats;
socklen_t tp_stats_len = sizeof(struct tpacket_stats_v3);
int ret;
ret = getsockopt(socket_fd, SOL_PACKET, PACKET_STATISTICS, &tp_stats, &tp_stats_len);
if ( ret < 0 ) {
Error(errno ? strerror(errno) : "unable to retrieve statistics");
s->received = s->bytes_received = s->link = s->dropped = 0;
return;
}
stats.link += tp_stats.tp_packets;
stats.dropped += tp_stats.tp_drops;
memcpy(s, &stats, sizeof(Stats));
}
zeek::iosource::PktSrc* AF_PacketSource::InstantiateAF_Packet(const std::string& path, bool is_live) {
return new AF_PacketSource(path, is_live);
}

View file

@ -1,82 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
extern "C" {
#include <errno.h> // errorno
#include <linux/if.h> // ifreq
#include <linux/if_packet.h> // AF_PACKET, etc.
#include <linux/net_tstamp.h> // hwtstamp_config
#include <linux/sockios.h> // SIOCSHWTSTAMP
#include <net/ethernet.h> // ETH_P_ALL
#include <pcap.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h> // close()
}
#include "zeek/iosource/PktSrc.h"
#include "zeek/iosource/af_packet/RX_Ring.h"
namespace zeek::iosource::pktsrc {
class AF_PacketSource : public zeek::iosource::PktSrc {
public:
/**
* Constructor.
*
* path: Name of the interface to open (the AF_Packet source doesn't
* support reading from files).
*
* is_live: Must be true (the AF_Packet source doesn't support offline
* operation).
*/
AF_PacketSource(const std::string& path, bool is_live);
/**
* Destructor.
*/
~AF_PacketSource() override;
static PktSrc* InstantiateAF_Packet(const std::string& path, bool is_live);
protected:
// PktSrc interface.
void Open() override;
void Close() override;
bool ExtractNextPacket(zeek::Packet* pkt) override;
void DoneWithPacket() override;
bool PrecompileFilter(int index, const std::string& filter) override;
bool SetFilter(int index) override;
void Statistics(Stats* stats) override;
private:
Properties props;
Stats stats;
int current_filter = 0;
unsigned int num_discarded = 0;
int checksum_mode = 0;
int socket_fd = -1;
RX_Ring* rx_ring = nullptr;
struct pcap_pkthdr current_hdr = {};
struct InterfaceInfo {
int index = -1;
int flags = 0;
bool Valid() { return index >= 0; }
bool IsUp() { return flags & IFF_UP; }
};
InterfaceInfo GetInterfaceInfo(const std::string& path);
bool BindInterface(const InterfaceInfo& info);
bool EnablePromiscMode(const InterfaceInfo& info);
bool ConfigureFanoutGroup(bool enabled, bool defrag);
bool ConfigureHWTimestamping(bool enabled);
uint32_t GetFanoutMode(bool defrag);
};
} // namespace zeek::iosource::pktsrc

View file

@ -1,5 +0,0 @@
if (${CMAKE_SYSTEM_NAME} MATCHES Linux)
set(ZEEK_HAVE_AF_PACKET yes CACHE INTERNAL "")
zeek_add_plugin(Zeek AF_Packet SOURCES Plugin.cc AF_Packet.cc RX_Ring.cc BIFS af_packet.bif)
endif ()

View file

@ -1,27 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "zeek/plugin/Plugin.h"
#include "zeek/iosource/Component.h"
#include "zeek/iosource/af_packet/AF_Packet.h"
namespace zeek::plugin::Zeek_AF_Packet {
class Plugin : public plugin::Plugin {
plugin::Configuration Configure() override {
AddComponent(
new ::zeek::iosource::PktSrcComponent("AF_PacketReader", "af_packet",
::zeek::iosource::PktSrcComponent::LIVE,
::zeek::iosource::pktsrc::AF_PacketSource::InstantiateAF_Packet));
zeek::plugin::Configuration config;
config.name = "Zeek::AF_Packet";
config.description = "Packet acquisition via AF_Packet";
config.version.major = 4;
config.version.minor = 0;
config.version.patch = 0;
return config;
}
} plugin;
} // namespace zeek::plugin::Zeek_AF_Packet

View file

@ -1,127 +0,0 @@
# Zeek::AF_Packet
This plugin provides native AF_Packet support for Zeek. For details about AF_Packet, see the corresponding [man page](http://man7.org/linux/man-pages/man7/packet.7.html).
> **Note**:
> Starting with Zeek version 5.2, Zeek ships with a built-in version of this plugin.
## Installation
Before installing the plugin, make sure your kernel supports PACKET_FANOUT[^1] and TPACKET_V3.
### Package Manager
The plugin is available as package for the [Zeek Package Manager](https://github.com/zeek/package-manager) and can be installed using the following command:
zkg install zeek-af_packet-plugin
### Manual Install
The following will compile and install the AF_Packet plugin alongside Zeek:
# ./configure && make && make install
If everything built and installed correctly, you should see this:
# zeek -NN Zeek::AF_Packet
Zeek::AF_Packet - Packet acquisition via AF_Packet (dynamic, version 4.0.0)
[Packet Source] AF_PacketReader (interface prefix "af_packet"; supports live input)
[Type] AF_Packet::FanoutMode
[Type] AF_Packet::ChecksumMode
[Constant] AF_Packet::buffer_size
[Constant] AF_Packet::block_size
[Constant] AF_Packet::block_timeout
[Constant] AF_Packet::enable_hw_timestamping
[Constant] AF_Packet::enable_defrag
[Constant] AF_Packet::enable_fanout
[Constant] AF_Packet::fanout_mode
[Constant] AF_Packet::fanout_id
[Constant] AF_Packet::link_type
[Constant] AF_Packet::checksum_validation_mode
## Usage
Once installed, you can use AF_Packet interfaces/ports by prefixing them with `af_packet::` on the command line. For example, to use AF_Packet to monitor interface `eth0`:
# zeek -i af_packet::eth0
### Permissions
To use AF_Packet, running Zeek without root privileges, the Zeek processes need the CAP_NET_RAW capability. You can set it with the following command (on each sensor, after `zeekctl install`):
# setcap cap_net_raw+eip <path_to_zeek>/bin/zeek
The AF_Packet plugin automatically enables promiscuous mode on the interfaces. As the plugin is using PACKET_ADD_MEMBERSHIP to enter the promiscuous mode without interfering others, the PROMISC flag is not touched. To verify that the interface entered promiscuous mode you can use `dmesg`.
### Offloading
Remember to disable any offloading features provided by the Network Interface Card (NIC) or Linux networking stack that interfere with Zeek. In general, Zeek expects to see network packets as they arrive on the wire. See this [blog post](https://blog.securityonion.net/2011/10/when-is-full-packet-capture-not-full.html) for more background. Toggling offloading features can be done with the `ethtool -K` command, for example:
# IFACE=eth0
# for offload in rx tx sg tso ufo gso gro lro; do
# ethtool -K $IFACE $offload off
# done
For more details around the involved offloads consult the [ethtool manpage](https://man7.org/linux/man-pages/man8/ethtool.8.html). In addition, `ethtool -S` can be used to gather statistics at the interface level.
While all offloading should usually be disabled, the plugin supports to outsource certain tasks like checksum validation. See the [configuration section](#advanced-configuration) for further information.
## Usage with `zeekctl`
To use the AF_Packet plugin with `zeekctl`, the `custom` load balance method can be utilized. The following shows an exemplary configuration:
[manager]
type=manager
host=localhost
[proxy-1]
type=proxy
host=localhost
[worker-1]
type=worker
host=localhost
interface=af_packet::eth0
lb_method=custom
lb_procs=8
pin_cpus=0,1,2,3,4,5,6,7
# Optional parameters for per node configuration:
af_packet_fanout_id=23
af_packet_fanout_mode=AF_Packet::FANOUT_HASH
af_packet_buffer_size=128*1024*1024
If all interfaces using `lb_method=custom` should be configured for AF_Packet, the prefix can be globally defined by adding the following line to `zeekctl.conf`:
lb_custom.InterfacePrefix=af_packet::
## Advanced Configuration
While the plugin aims at providing a "plug and play" user experience, it exposes several configuration options of the underlying API for customization (see [init.zeek](scripts/init.zeek) for the default values):
* `buffer_size`: Set the overall buffer size allocated per socket. As the buffer is divided into blocks, this should be a multiple of the block size.
* `block_size`: Set the size of a block in the buffer. Instead of ingesting packet by packet into the application, packets are aggregated in blocks to improve performance. The block size **must** be a multiple of the system's page size (see `getconf PAGE_SIZE`).
* `block_timeout`: Set the timeout in milliseconds for passing a block to the application. This can be useful to reduce latency on less busy links.
* `enable_hw_timestamping`: Enable support for hardware timestamping. Please note that this is an experimental feature.
* `enable_defrag`: Enable defragmentation of IP packets before packets are load-balanced. This can be useful to prevent different fragments from being sent to different workers.
* `enable_fanout`: Enable packet fanout for load-balancing across multiple workers. The load-balancing strategy is determined by the fanout mode.
* `fanout_mode`: Set the load-balancing strategy. See [af_packet.bif](af_packet.bif) for the supported fanout modes.
* `fanout_id`: Set the fanout ID that identifies a load-balancing group. When monitoring multiple interfaces, a separate ID has to be configured for each interface.
* `link_type`: Set the link layer protocol.
* `checksum_validation_mode`: Set how checksums are calculated and verified. See [af_packet.bif](af_packet.bif) for the supported validation modes.
> **Note**:
> Setting `checksum_validation_mode` will not have any effect when used with Zeek prior version 5.1.
For further details on the above configuration options see the [kernel documentation](https://docs.kernel.org/networking/packet_mmap.html). Actual performance tuning is rather an art. For in-depth guidance, see the following resources, which can be transferred to Zeek:
* [Suricata Extreme Performance Tuning guide](https://github.com/pevma/SEPTun)
* [Suricata Extreme Performance Tuning guide - Mark II](https://github.com/pevma/SEPTun-Mark-II)
## Limitations
* __VLAN tagging is now supported.__ Even using AF_Packet's ``ETH_P_ALL``, the kernel removes VLAN tags from packets.
~~While the tags are provided spereately, there is no efficient way to pass them to Zeek.~~ Applying knowledge about the internal data structures used by Zeek, the plugin now forwards VLAN tag control information to Zeek. Both IEEE 802.1Q and IEEE 802.1ad (QinQ) will be handled as expected.
* Zeek workers crashing or restarting can, for a short period of time, disturb load balancing due to their packet
sockets being removed and later rejoining the fanout group. This may be visible in Zeek logs as gaps and/or duplicated connection entries produced by different Zeek workers.
[^1]: Note that some kernel versions between 3.10 and 4.7 might exhibit a bug that prevents the required symmetric hashing. The script available at https://github.com/JustinAzoff/can-i-use-afpacket-fanout can be used to verify whether PACKET_FANOUT works as expected. This issue should have been fixed in all stable kernels by now.

View file

@ -1,97 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "zeek/iosource/af_packet/RX_Ring.h"
#include <cstring>
#include <utility>
extern "C" {
#include <linux/if_packet.h> // AF_PACKET, etc.
#include <sys/mman.h> // mmap
#include <sys/socket.h> // socketopt consts
#include <unistd.h> // sysconf
}
RX_Ring::RX_Ring(int sock, size_t bufsize, size_t blocksize, int blocktimeout_msec) {
if ( sock < 0 )
throw RX_RingException("invalid socket");
// Configure socket
int ver = TPACKET_VERSION;
if ( setsockopt(sock, SOL_PACKET, PACKET_VERSION, &ver, sizeof(ver)) != 0 )
throw RX_RingException("unable to set TPacket version");
InitLayout(bufsize, blocksize, blocktimeout_msec);
if ( setsockopt(sock, SOL_PACKET, PACKET_RX_RING, (uint8_t*)&layout, sizeof(layout)) != 0 )
throw RX_RingException("unable to set ring layout");
// Map memory
size = static_cast<size_t>(layout.tp_block_size) * layout.tp_block_nr;
ring = (uint8_t*)mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, sock, 0);
if ( ring == MAP_FAILED )
throw RX_RingException("unable to map ring memory");
block_num = packet_num = 0;
packet = nullptr;
// Init block mapping
blocks = new tpacket_block_desc*[layout.tp_block_nr];
for ( size_t i = 0; i < layout.tp_block_nr; i++ )
blocks[i] = (struct tpacket_block_desc*)(ring + i * layout.tp_block_size);
}
RX_Ring::~RX_Ring() {
ReleasePacket();
delete[] blocks;
munmap(ring, size);
blocks = nullptr;
size = 0;
}
bool RX_Ring::GetNextPacket(tpacket3_hdr** hdr) {
struct tpacket_hdr_v1* block_hdr = &(blocks[block_num]->hdr.bh1);
if ( (block_hdr->block_status & TP_STATUS_USER) == 0 )
return false;
if ( packet == nullptr ) {
// New block
packet_num = block_hdr->num_pkts;
if ( packet_num == 0 ) {
NextBlock();
return false;
}
packet = (struct tpacket3_hdr*)((uint8_t*)blocks[block_num] + block_hdr->offset_to_first_pkt);
}
else
// Continue with block
packet = (struct tpacket3_hdr*)((uint8_t*)packet + packet->tp_next_offset);
*hdr = packet;
packet_num--;
return true;
}
void RX_Ring::ReleasePacket() {
if ( packet_num == 0 )
NextBlock();
}
void RX_Ring::InitLayout(size_t bufsize, size_t blocksize, int blocktimeout_msec) {
memset(&layout, 0, sizeof(layout));
layout.tp_block_size = blocksize;
layout.tp_frame_size = TPACKET_ALIGNMENT << 7; // Seems to be irrelevant for V3
layout.tp_block_nr = bufsize / layout.tp_block_size;
layout.tp_frame_nr = (layout.tp_block_size / layout.tp_frame_size) * layout.tp_block_nr;
layout.tp_retire_blk_tov = blocktimeout_msec;
}
void RX_Ring::NextBlock() {
struct tpacket_hdr_v1* block_hdr = &(blocks[block_num]->hdr.bh1);
block_hdr->block_status = TP_STATUS_KERNEL;
block_num = (block_num + 1) % layout.tp_block_nr;
packet = nullptr;
}

View file

@ -1,44 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
extern "C" {
#include <linux/if_packet.h> // AF_PACKET, etc.
}
#include <cstdint>
#include <stdexcept>
#define TPACKET_VERSION TPACKET_V3
class RX_RingException : public std::runtime_error {
public:
RX_RingException(const std::string& what_arg) : std::runtime_error(what_arg) {}
};
class RX_Ring {
public:
/**
* Constructor
*/
RX_Ring(int sock, size_t bufsize, size_t blocksize, int blocktimeout_msec);
~RX_Ring();
bool GetNextPacket(tpacket3_hdr** hdr);
void ReleasePacket();
protected:
void InitLayout(size_t bufsize, size_t blocksize, int blocktimeout_msec);
void NextBlock();
private:
struct tpacket_req3 layout;
struct tpacket_block_desc** blocks;
struct tpacket3_hdr* packet;
unsigned int block_num;
unsigned int packet_num;
uint8_t* ring;
size_t size;
};

View file

@ -1,15 +0,0 @@
# Options for the AF_Packet packet source.
module AF_Packet;
const buffer_size: count;
const block_size: count;
const block_timeout: interval;
const enable_hw_timestamping: bool;
const enable_defrag: bool;
const enable_fanout: bool;
const fanout_mode: FanoutMode;
const fanout_id: count;
const link_type: count;
const checksum_validation_mode: ChecksumMode;

View file

@ -1251,7 +1251,7 @@ function hexdump%(data_str: string%) : string
function reverse%(str: string%) : string
%{
string s = str->ToStdString();
std::ranges::reverse(s);
reverse(s.begin(), s.end());
return zeek::make_intrusive<zeek::StringVal>(s.length(), (const char*)s.c_str());
%}
@ -1308,8 +1308,8 @@ static int64_t do_find_str(zeek::StringVal* str, zeek::StringVal* sub, int64_t s
if ( ! case_sensitive )
{
std::ranges::transform(s, s.begin(), ::tolower);
std::ranges::transform(sb, sb.begin(), ::tolower);
transform(s.begin(), s.end(), s.begin(), ::tolower);
transform(sb.begin(), sb.end(), sb.begin(), ::tolower);
}
if ( rfind )

View file

@ -73,7 +73,7 @@ bool labels_valid(std::span<const zeek::telemetry::LabelView> labels,
return std::find(keys.begin(), keys.end(), x.first) != keys.end();
};
return labels.size() == label_names.size()
&& std::ranges::all_of(labels, key_in_label_names);
&& std::all_of(labels.begin(), labels.end(), key_in_label_names);
}
template <class IntOrDbl>

View file

@ -247,26 +247,4 @@ enum Level %{
ERROR = 2,
%}
module AF_Packet;
## Available fanout modes.
enum FanoutMode %{
FANOUT_HASH, # PACKET_FANOUT_HASH
FANOUT_CPU, # PACKET_FANOUT_CPU
FANOUT_QM, # PACKET_FANOUT_QM
FANOUT_CBPF, # PACKET_FANOUT_CBPF
FANOUT_EBPF, # PACKET_FANOUT_EBPF
%}
## Available checksum validation modes.
enum ChecksumMode %{
## Ignore checksums, i.e. always assume they are correct.
CHECKSUM_OFF,
## Let Zeek compute and verify checksums.
CHECKSUM_ON,
## Let the kernel handle checksum offloading.
## Note: Semantics may depend on the kernel and driver version.
CHECKSUM_KERNEL,
%}
module GLOBAL;

View file

@ -17,7 +17,6 @@
#include <cstring>
#include <ctime>
#include <vector>
#include <numbers>
#include "zeek/digest.h"
#include "zeek/Reporter.h"
@ -4223,13 +4222,12 @@ function blocking_lookup_hostname%(host: string%) : addr_set
## .. zeek:see:: haversine_distance_ip
function haversine_distance%(lat1: double, long1: double, lat2: double, long2: double%): double
%{
constexpr double RADIUS = 3958.7615; // Mean radius of the Earth in miles.
constexpr double PI_360 = std::numbers::pi / 360.0;
constexpr double PI_180 = std::numbers::pi / 180.0;
const double PI = 3.14159;
const double RADIUS = 3958.8; // Earth's radius in miles.
double s1 = pow(sin((lat2 - lat1) * PI_360), 2);
double s2 = pow(sin((long2 - long1) * PI_360), 2);
double a = s1 + (cos(lat1 * PI_180) * cos(lat2 * PI_180) * s2);
double s1 = sin((lat2 - lat1) * PI/360);
double s2 = sin((long2 - long1) * PI/360);
double a = s1 * s1 + cos(lat1 * PI/180) * cos(lat2 * PI/180) * s2 * s2;
double distance = 2 * RADIUS * asin(sqrt(a));
return zeek::make_intrusive<zeek::DoubleVal>(distance);

View file

@ -1,8 +1,8 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
5.8480e+03
5.8480e+03
5.8481e+03
5.8481e+03
1.9193e-02
1.5124e-02
9.0762e-01
1.5136e-02
9.2419e-01
1.2437e+04
1.2437e+04

View file

@ -18,7 +18,7 @@ event zeek_init()
test(.0001388889, 0, -.0001388889, 0);
# Distance of one second of longitude (crossing the prime meridian).
test(38, 0.000138889, 38, -0.000138889);
test(38, 0.000138999, 38, -0.000138999);
# Distance of one minute of longitude (test extreme longitude values).
test(38, 180, 38, -179.98333);

View file

@ -1,26 +0,0 @@
find_package(BISON REQUIRED)
find_package(FLEX REQUIRED)
set(BISON_FLAGS "--debug")
# BIF parser/scanner
bison_target(BIFParser builtin-func.y ${CMAKE_CURRENT_BINARY_DIR}/bif_parse.cc
DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/bif_parse.h COMPILE_FLAGS "${BISON_FLAGS}")
flex_target(BIFScanner builtin-func.l ${CMAKE_CURRENT_BINARY_DIR}/bif_lex.cc)
add_flex_bison_dependency(BIFScanner BIFParser)
set(bifcl_SRCS ${BISON_BIFParser_INPUT} ${FLEX_BIFScanner_INPUT} ${BISON_BIFParser_OUTPUTS}
${FLEX_BIFScanner_OUTPUTS} bif_arg.cc module_util.cc)
add_executable(bifcl ${bifcl_SRCS})
target_include_directories(bifcl BEFORE PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
if (MSVC)
target_compile_options(bifcl PUBLIC "/J") # Similar to -funsigned-char on other platforms
target_compile_options(bifcl PUBLIC "/wd4018") # Similar to -Wno-sign-compare on other platforms
target_link_libraries(bifcl PRIVATE libunistd)
else ()
target_compile_options(bifcl PUBLIC "-Wno-sign-compare")
endif ()
install(TARGETS bifcl DESTINATION bin)

View file

@ -1,19 +0,0 @@
.. _Zeek: https://www.zeek.org
=================
Zeek BIF Compiler
=================
The ``bifcl`` program simply takes a ``.bif`` file as input and
generates C++ header/source files along with a ``.zeek`` script
that all-together provide the declaration and implementation of Zeek_
Built-In-Functions (BIFs), which can then be compiled and shipped
as part of a Zeek plugin.
A BIF allows one to write arbitrary C++ code and access it via a
function call inside a Zeek script. In this way, they can also be
used to access parts of Zeek's internal C++ API that aren't already
exposed via their own BIFs.
At the moment, learning the format of a ``.bif`` file is likely easiest
by just taking a look at the ``.bif`` files inside the Zeek source-tree.

View file

@ -1,82 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "bif_arg.h"
#include <cstring>
static struct {
const char* type_enum;
const char* bif_type;
const char* zeek_type;
const char* c_type;
const char* c_type_smart;
const char* accessor;
const char* accessor_smart;
const char* cast_smart;
const char* constructor;
const char* ctor_smart;
} builtin_func_arg_type[] = {
#define DEFINE_BIF_TYPE(id, bif_type, zeek_type, c_type, c_type_smart, accessor, accessor_smart, cast_smart, \
constructor, ctor_smart) \
{#id, bif_type, zeek_type, c_type, c_type_smart, accessor, accessor_smart, cast_smart, constructor, ctor_smart},
#include "bif_type.def"
#undef DEFINE_BIF_TYPE
};
extern const char* arg_list_name;
BuiltinFuncArg::BuiltinFuncArg(const char* arg_name, int arg_type) {
name = arg_name;
type = arg_type;
type_str = "";
attr_str = "";
}
BuiltinFuncArg::BuiltinFuncArg(const char* arg_name, const char* arg_type_str, const char* arg_attr_str) {
name = arg_name;
type = TYPE_OTHER;
type_str = arg_type_str;
attr_str = arg_attr_str;
for ( int i = 0; builtin_func_arg_type[i].bif_type[0] != '\0'; ++i )
if ( ! strcmp(builtin_func_arg_type[i].bif_type, arg_type_str) ) {
type = i;
type_str = "";
}
}
void BuiltinFuncArg::PrintZeek(FILE* fp) {
fprintf(fp, "%s: %s%s %s", name, builtin_func_arg_type[type].zeek_type, type_str, attr_str);
}
void BuiltinFuncArg::PrintCDef(FILE* fp, int n, bool runtime_type_check) {
// Generate a runtime type-check pre-amble for types we understand
if ( runtime_type_check && type != TYPE_OTHER && type != TYPE_ANY ) {
fprintf(fp, "\t\t{\n");
fprintf(fp, "\t\t// Runtime type check for %s argument\n", name);
fprintf(fp, "\t\tzeek::TypeTag __tag = (*%s)[%d]->GetType()->Tag();\n", arg_list_name, n);
fprintf(fp, "\t\tif ( __tag != %s )\n", builtin_func_arg_type[type].type_enum);
fprintf(fp, "\t\t\t{\n");
fprintf(fp,
"\t\t\tzeek::emit_builtin_error(zeek::util::fmt(\"expected type %s for %s, got "
"%%s\", zeek::type_name(__tag)));\n",
builtin_func_arg_type[type].zeek_type, name);
fprintf(fp, "\t\t\treturn nullptr;\n");
fprintf(fp, "\t\t\t}\n");
fprintf(fp, "\t\t}\n");
}
fprintf(fp, "\t%s %s = (%s) (", builtin_func_arg_type[type].c_type, name, builtin_func_arg_type[type].c_type);
char buf[1024];
snprintf(buf, sizeof(buf), "(*%s)[%d].get()", arg_list_name, n);
// Print the accessor expression.
fprintf(fp, builtin_func_arg_type[type].accessor, buf);
fprintf(fp, ");\n");
}
void BuiltinFuncArg::PrintCArg(FILE* fp, int n) {
fprintf(fp, "%s %s", builtin_func_arg_type[type].c_type_smart, name);
}
void BuiltinFuncArg::PrintValConstructor(FILE* fp) { fprintf(fp, builtin_func_arg_type[type].ctor_smart, name); }

View file

@ -1,469 +0,0 @@
%top{
// Include cstdint at the start of the generated file. Typically
// MSVC will include this header later, after the definitions of
// the integral type macros. MSVC then complains that about the
// redefinition of the types. Including cstdint early avoids this.
#include <cstdint>
}
%{
// See the file "COPYING" in the main distribution directory for copyright.
#include <ctype.h>
#include <unistd.h>
#include <cstring>
#include <memory>
#include "bif_arg.h"
#include "bif_parse.h"
char* copy_string(const char* s)
{
char* c = new char[strlen(s)+1];
strcpy(c, s);
return c;
}
int line_number = 1;
extern bool in_c_code;
int check_c_mode(int t)
{
if ( ! in_c_code )
return t;
yylval.str = copy_string(yytext);
return TOK_C_TOKEN;
}
%}
WS [ \t]+
OWS [ \t]*
IDCOMPONENT [A-Za-z_][A-Za-z_0-9]*
ID {IDCOMPONENT}(::{IDCOMPONENT})*
ESCSEQ (\\([^\n]|[0-7]+|x[[:xdigit:]]+))
DEC [[:digit:]]+
HEX [0-9a-fA-F]+
%option nodefault
%%
#.* {
yylval.str = copy_string(yytext);
return TOK_COMMENT;
}
\n {
++line_number;
return TOK_LF;
}
{WS} {
yylval.str = copy_string(yytext);
return TOK_WS;
}
[=,:;] return check_c_mode(yytext[0]);
"%{" return TOK_LPB;
"%}" return TOK_RPB;
"%%{" return TOK_LPPB;
"%%}" return TOK_RPPB;
"%(" return check_c_mode(TOK_LPP);
"%)" return check_c_mode(TOK_RPP);
"..." return check_c_mode(TOK_VAR_ARG);
"function" return check_c_mode(TOK_FUNCTION);
"event" return check_c_mode(TOK_EVENT);
"const" return check_c_mode(TOK_CONST);
"enum" return check_c_mode(TOK_ENUM);
"type" return check_c_mode(TOK_TYPE);
"record" return check_c_mode(TOK_RECORD);
"set" return check_c_mode(TOK_SET);
"table" return check_c_mode(TOK_TABLE);
"vector" return check_c_mode(TOK_VECTOR);
"of" return check_c_mode(TOK_OF);
"opaque" return check_c_mode(TOK_OPAQUE);
"module" return check_c_mode(TOK_MODULE);
"@ARG@" return TOK_ARG;
"@ARGS@" return TOK_ARGS;
"@ARGC@" return TOK_ARGC;
"T" yylval.val = 1; return TOK_BOOL;
"F" yylval.val = 0; return TOK_BOOL;
{DEC} {
yylval.str = copy_string(yytext);
return TOK_INT;
}
"0x"{HEX} {
yylval.str = copy_string(yytext);
return TOK_INT;
}
{ID} {
yylval.str = copy_string(yytext);
return TOK_ID;
}
/*
Hacky way to pass along arbitrary attribute expressions since the BIF parser
has little understanding of valid Zeek expressions. With this pattern, the
attribute expression should stop when it reaches another attribute, another
function argument, or the end of the function declaration.
*/
&{ID}({OWS}={OWS}[^&%;,]+)? {
int t = check_c_mode(TOK_ATTR);
if ( t == TOK_ATTR )
{
yylval.str = copy_string(yytext);
return TOK_ATTR;
}
else
return t;
}
\"([^\\\n\"]|{ESCSEQ})*\" {
yylval.str = copy_string(yytext);
return TOK_CSTR;
}
\'([^\\\n\']|{ESCSEQ})*\' {
yylval.str = copy_string(yytext);
return TOK_CSTR;
}
. {
yylval.val = yytext[0];
return TOK_ATOM;
}
%%
int yywrap()
{
yy_delete_buffer(YY_CURRENT_BUFFER);
return 1;
}
extern int yyparse();
char* input_filename = nullptr;
char* input_filename_with_path = nullptr;
char* plugin = nullptr;
bool alternative_mode = false;
FILE* fp_zeek_init = nullptr;
FILE* fp_func_def = nullptr;
FILE* fp_func_h = nullptr;
FILE* fp_func_init = nullptr;
FILE* fp_func_register = nullptr;
FILE* fp_netvar_h = nullptr;
FILE* fp_netvar_def = nullptr;
FILE* fp_netvar_init = nullptr;
void remove_file(const char *surfix);
void err_exit(void);
FILE* open_output_file(const char* surfix);
void close_if_open(FILE **fpp);
void close_all_output_files(void);
FILE* open_output_file(const char* surfix)
{
char fn[1024];
FILE* fp;
snprintf(fn, sizeof(fn), "%s.%s", input_filename, surfix);
if ( (fp = fopen(fn, "w")) == NULL )
{
fprintf(stderr, "Error: cannot open file: %s\n", fn);
err_exit();
}
return fp;
}
void usage()
{
fprintf(stderr, "usage: bifcl [-p <plugin> | -s] *.bif\n");
exit(1);
}
void init_alternative_mode()
{
fp_zeek_init = open_output_file("zeek");
fp_func_h = open_output_file("h");
fp_func_def = open_output_file("cc");
fp_func_init = open_output_file("init.cc");
fp_func_register = plugin ? open_output_file("register.cc") : nullptr;
fp_netvar_h = fp_func_h;
fp_netvar_def = fp_func_def;
fp_netvar_init = fp_func_init;
int n = 1024 + strlen(input_filename);
auto auto_gen_comment_buf = std::make_unique<char[]>(n);
auto auto_gen_comment = auto_gen_comment_buf.get();
snprintf(auto_gen_comment, n,
"This file was automatically generated by bifcl from %s (%s mode).",
input_filename_with_path, plugin ? "plugin" : "alternative");
fprintf(fp_zeek_init, "# %s\n\n", auto_gen_comment);
fprintf(fp_func_def, "// %s\n\n", auto_gen_comment);
fprintf(fp_func_h, "// %s\n\n", auto_gen_comment);
fprintf(fp_func_h, "#pragma once\n\n");
fprintf(fp_func_init, "// %s\n\n", auto_gen_comment);
if ( fp_func_register )
fprintf(fp_func_register, "// %s\n\n", auto_gen_comment);
static char guard[1024];
if ( getcwd(guard, sizeof(guard)) == NULL )
{
fprintf(stderr, "Error: cannot get current working directory\n");
err_exit();
}
strncat(guard, "/", sizeof(guard) - strlen(guard) - 1);
strncat(guard, input_filename, sizeof(guard) - strlen(guard) - 1);
for ( char* p = guard; *p; p++ )
{
if ( ! isalnum(*p) )
*p = '_';
}
fprintf(fp_func_h, "#if defined(ZEEK_IN_NETVAR) || ! defined(%s)\n", guard);
fprintf(fp_func_h, "#ifndef ZEEK_IN_NETVAR\n");
fprintf(fp_func_h, "#ifndef %s\n", guard);
fprintf(fp_func_h, "#define %s\n", guard);
fprintf(fp_func_h, "#include \"zeek/zeek-bif.h\"\n");
fprintf(fp_func_h, "#endif\n");
fprintf(fp_func_h, "#endif\n");
fprintf(fp_func_h, "\n");
fprintf(fp_func_def, "\n");
fprintf(fp_func_def, "#include \"%s.h\"\n", input_filename);
fprintf(fp_func_def, "#include \"zeek/Func.h\"\n");
fprintf(fp_func_def, "\n");
static char name[1024];
strncpy(name, input_filename, sizeof(name) - 1);
name[sizeof(name) - 1] = '\0';
char* dot = strchr(name, '.');
if ( dot )
*dot = '\0';
if ( plugin )
{
static char plugin_canon[1024];
strncpy(plugin_canon, plugin, sizeof(plugin_canon) - 1);
plugin_canon[sizeof(plugin_canon) - 1] = '\0';
char* colon = strstr(plugin_canon, "::");
if ( colon ) {
*colon = '_';
memmove(colon + 1, colon + 2, plugin_canon + strlen(plugin_canon) - colon);
}
fprintf(fp_func_init, "\n");
fprintf(fp_func_init, "#include <list>\n");
fprintf(fp_func_init, "#include <string>\n");
fprintf(fp_func_init, "#include \"zeek/plugin/Plugin.h\"\n");
fprintf(fp_func_init, "#include \"zeek/Func.h\"\n");
fprintf(fp_func_init, "#include \"%s.h\"\n", input_filename);
fprintf(fp_func_init, "\n");
fprintf(fp_func_init, "namespace plugin::%s {\n", plugin_canon);
fprintf(fp_func_init, "\n");
fprintf(fp_func_init, "void __bif_%s_init(zeek::plugin::Plugin* plugin)\n", name);
fprintf(fp_func_init, "\t{\n");
fprintf(fp_func_register, "#include \"zeek/plugin/Manager.h\"\n");
fprintf(fp_func_register, "\n");
fprintf(fp_func_register, "namespace plugin::%s {\n", plugin_canon);
fprintf(fp_func_register, "void __bif_%s_init(zeek::plugin::Plugin* plugin);\n", name);
fprintf(fp_func_register, "zeek::plugin::detail::__RegisterBif __register_bifs_%s_%s(\"%s\", __bif_%s_init);\n", plugin_canon, name, plugin, name);
fprintf(fp_func_register, "}\n");
}
}
void finish_alternative_mode()
{
fprintf(fp_func_h, "\n");
fprintf(fp_func_h, "#endif\n");
if ( plugin )
{
fprintf(fp_func_init, "\n");
fprintf(fp_func_init, "\t}\n");
fprintf(fp_func_init, "}\n");
fprintf(fp_func_init, "\n");
fprintf(fp_func_init, "\n");
}
}
// GCC uses __SANITIZE_ADDRESS__, Clang uses __has_feature
#if defined(__SANITIZE_ADDRESS__)
#define USING_ASAN
#endif
#if defined(__has_feature)
#if __has_feature(address_sanitizer)
#define USING_ASAN
#endif
#endif
// FreeBSD doesn't support LeakSanitizer
#if defined(USING_ASAN) && !defined(__FreeBSD__)
#include <sanitizer/lsan_interface.h>
#define BIFCL_LSAN_DISABLE() __lsan_disable()
#else
#define BIFCL_LSAN_DISABLE()
#endif
int main(int argc, char* argv[])
{
// We generally do not care at all if bifcl is leaking and the default
// behavior of LSAN to treat leaks as errors only trips up Zeek's build.
BIFCL_LSAN_DISABLE();
int opt;
while ( (opt = getopt(argc, argv, "p:s")) != -1 )
{
switch ( opt ) {
case 'p':
alternative_mode = true;
plugin = (char*) optarg;
break;
case 's':
alternative_mode = true;
break;
default:
usage();
}
}
for ( int i = optind; i < argc; i++ )
{
FILE* fp_input;
input_filename = input_filename_with_path = argv[i];
char* slash = strrchr(input_filename, '/');
if ( (fp_input = fopen(input_filename, "r")) == NULL )
{
fprintf(stderr, "Error: cannot open file: %s\n", input_filename);
/* no output files open. can simply exit */
exit(1);
}
if ( slash )
input_filename = slash + 1;
if ( ! alternative_mode )
{
fp_zeek_init = open_output_file("zeek");
fp_func_h = open_output_file("func_h");
fp_func_def = open_output_file("func_def");
fp_func_init = open_output_file("func_init");
fp_netvar_h = open_output_file("netvar_h");
fp_netvar_def = open_output_file("netvar_def");
fp_netvar_init = open_output_file("netvar_init");
int n = 1024 + strlen(input_filename);
auto auto_gen_comment_buf = std::make_unique<char[]>(n);
auto auto_gen_comment = auto_gen_comment_buf.get();
snprintf(auto_gen_comment, n,
"This file was automatically generated by bifcl from %s.",
input_filename);
fprintf(fp_zeek_init, "# %s\n\n", auto_gen_comment);
fprintf(fp_func_def, "// %s\n\n", auto_gen_comment);
fprintf(fp_func_h, "// %s\n\n", auto_gen_comment);
fprintf(fp_func_h, "#pragma once\n\n");
fprintf(fp_func_init, "// %s\n\n", auto_gen_comment);
fprintf(fp_netvar_def, "// %s\n\n", auto_gen_comment);
fprintf(fp_netvar_h, "// %s\n\n", auto_gen_comment);
fprintf(fp_netvar_h, "#pragma once\n\n");
fprintf(fp_netvar_init, "// %s\n\n", auto_gen_comment);
}
else
init_alternative_mode();
fprintf(fp_netvar_init, "#ifdef __GNUC__\n");
fprintf(fp_netvar_init, "#pragma GCC diagnostic push\n");
fprintf(fp_netvar_init, "#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"\n\n");
fprintf(fp_netvar_init, "#endif\n");
yy_switch_to_buffer(yy_create_buffer(fp_input, YY_BUF_SIZE));
yyparse();
fprintf(fp_netvar_init, "#ifdef __GNUC__\n");
fprintf(fp_netvar_init, "\n\n#pragma GCC diagnostic pop\n");
fprintf(fp_netvar_init, "#endif\n");
if ( alternative_mode )
finish_alternative_mode();
fclose(fp_input);
close_all_output_files();
}
}
void close_if_open(FILE **fpp)
{
if (*fpp)
fclose(*fpp);
*fpp = nullptr;
}
void close_all_output_files(void)
{
close_if_open(&fp_zeek_init);
close_if_open(&fp_func_h);
close_if_open(&fp_func_def);
close_if_open(&fp_func_init);
close_if_open(&fp_func_register);
if ( ! alternative_mode )
{
close_if_open(&fp_netvar_h);
close_if_open(&fp_netvar_def);
close_if_open(&fp_netvar_init);
}
}
void remove_file(const char *surfix)
{
char fn[1024];
snprintf(fn, sizeof(fn), "%s.%s", input_filename, surfix);
unlink(fn);
}
void err_exit(void)
{
close_all_output_files();
/* clean up. remove all output files we've generated so far */
remove_file("zeek");
remove_file("func_h");
remove_file("func_def");
remove_file("func_init");
remove_file("func_register");
remove_file("netvar_h");
remove_file("netvar_def");
remove_file("netvar_init");
exit(1);
}

View file

@ -1,837 +0,0 @@
%{
// See the file "COPYING" in the main distribution directory for copyright.
#include <cinttypes>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <set>
#include <string>
#include "module_util.h"
using namespace std;
extern int line_number;
extern char* input_filename;
extern char* input_filename_with_path;
extern char* plugin;
extern bool alternative_mode;
#define print_line_directive(fp) fprintf(fp, "\n#line %d \"%s\"\n", line_number, input_filename_with_path)
extern FILE* fp_zeek_init;
extern FILE* fp_func_def;
extern FILE* fp_func_h;
extern FILE* fp_func_init;
extern FILE* fp_netvar_h;
extern FILE* fp_netvar_def;
extern FILE* fp_netvar_init;
bool in_c_code = false;
string current_module = GLOBAL_MODULE_NAME;
int definition_type;
string type_name;
// Alternate event prototypes are only written to the .zeek file, but
// don't need any further changes to C++ source/header files, so this
// set keeps track of whether the first event prototype information has
// already been defined/written to the C++ files.
static std::set<std::string> events;
enum : uint8_t {
C_SEGMENT_DEF,
FUNC_DEF,
EVENT_DEF,
TYPE_DEF,
CONST_DEF,
};
// Holds the name of a declared object (function, enum, record type, event,
// etc. and information about namespaces, etc.
struct decl_struct {
string module_name;
string bare_name; // name without module or namespace
string c_namespace_start; // "opening" namespace for use in netvar_*
string c_namespace_end; // closing "}" for all the above namespaces
string c_fullname; // fully qualified name (namespace::....) for use in netvar_init
string zeek_fullname; // fully qualified zeek name, for netvar (and lookup_ID())
string zeek_name; // the name as we read it from input. What we write into the .zeek file
// special cases for events. Events have an EventHandlerPtr
// and a enqueue_* function. This name is for the enqueue_* function
string enqueue_c_namespace_start;
string enqueue_c_namespace_end;
string enqueue_c_barename;
string enqueue_c_fullname;
} decl;
void set_definition_type(int type, const char *arg_type_name)
{
definition_type = type;
if ( type == TYPE_DEF && arg_type_name )
type_name = string(arg_type_name);
else
type_name = "";
}
void set_decl_name(const char *name)
{
decl.bare_name = extract_var_name(name);
// make_full_var_name prepends the correct module, if any
// then we can extract the module name again.
string varname = make_full_var_name(current_module.c_str(), name);
decl.module_name = extract_module_name(varname.c_str());
decl.c_namespace_start = "";
decl.c_namespace_end = "";
decl.c_fullname = "";
decl.zeek_fullname = "";
decl.zeek_name = "";
decl.enqueue_c_fullname = "";
decl.enqueue_c_barename = string("enqueue_") + decl.bare_name;
decl.enqueue_c_namespace_start = "";
decl.enqueue_c_namespace_end = "";
switch ( definition_type ) {
case TYPE_DEF:
decl.c_namespace_start = "BifType::" + type_name + "";
decl.c_fullname = "BifType::" + type_name + "::";
break;
case CONST_DEF:
decl.c_namespace_start = "BifConst";
decl.c_fullname = "BifConst::";
break;
case FUNC_DEF:
decl.c_namespace_start = "BifFunc";
decl.c_fullname = "BifFunc::";
break;
case EVENT_DEF:
decl.c_namespace_start = "";
decl.c_namespace_end = "";
decl.c_fullname = "::"; // need this for namespace qualified events due do event_c_body
decl.enqueue_c_namespace_start = "BifEvent";
decl.enqueue_c_fullname = "zeek::BifEvent::";
break;
default:
break;
}
if ( decl.module_name != GLOBAL_MODULE_NAME )
{
if ( decl.c_namespace_start.empty() ) {
decl.c_namespace_start += "namespace " + decl.module_name + " { ";
decl.c_namespace_end += " }";
}
else {
decl.c_namespace_start += "::" + decl.module_name;
decl.c_namespace_end = "";
}
decl.c_fullname += decl.module_name + "::";
decl.zeek_fullname += decl.module_name + "::";
if ( decl.enqueue_c_namespace_start.empty() ) {
decl.enqueue_c_namespace_start += "namespace " + decl.module_name + " { ";
decl.enqueue_c_namespace_end += " } ";
}
else {
decl.enqueue_c_namespace_start += "::" + decl.module_name;
decl.enqueue_c_namespace_end = "";
}
decl.enqueue_c_fullname += decl.module_name + "::";
}
decl.zeek_fullname += decl.bare_name;
decl.c_fullname += decl.bare_name;
decl.zeek_name += name;
decl.enqueue_c_fullname += decl.enqueue_c_barename;
}
const char* arg_list_name = "BiF_ARGS";
#include "bif_arg.h"
/* Map bif/zeek type names to C types for use in const declaration */
static struct {
const char* bif_type;
const char* zeek_type;
const char* c_type;
const char* c_type_smart;
const char* accessor;
const char* accessor_smart;
const char* cast_smart;
const char* constructor;
const char* ctor_smatr;
} builtin_types[] = {
#define DEFINE_BIF_TYPE(id, bif_type, zeek_type, c_type, c_type_smart, accessor, accessor_smart, cast_smart, constructor, ctor_smart) \
{bif_type, zeek_type, c_type, c_type_smart, accessor, accessor_smart, cast_smart, constructor, ctor_smart},
#include "bif_type.def"
#undef DEFINE_BIF_TYPE
};
int get_type_index(const char *type_name)
{
for ( int i = 0; builtin_types[i].bif_type[0] != '\0'; ++i )
{
if ( strcmp(builtin_types[i].bif_type, type_name) == 0 )
return i;
}
return TYPE_OTHER;
}
int var_arg; // whether the number of arguments is variable
std::vector<BuiltinFuncArg*> args;
extern int yyerror(const char[]);
extern int yywarn(const char msg[]);
extern int yylex();
char* concat(const char* str1, const char* str2)
{
int len1 = strlen(str1);
int len2 = strlen(str2);
char* s = new char[len1 + len2 +1];
memcpy(s, str1, len1);
memcpy(s + len1, str2, len2);
s[len1+len2] = '\0';
return s;
}
static void print_event_c_prototype_args(FILE* fp)
{
for ( auto i = 0u; i < args.size(); ++i )
{
if ( i > 0 )
fprintf(fp, ", ");
args[i]->PrintCArg(fp, i);
}
}
static void print_event_c_prototype_header(FILE* fp)
{
fprintf(fp, "namespace zeek::%s { void %s(zeek::analyzer::Analyzer* analyzer%s",
decl.enqueue_c_namespace_start.c_str(),
decl.enqueue_c_barename.c_str(),
args.size() ? ", " : "" );
print_event_c_prototype_args(fp);
fprintf(fp, ")");
fprintf(fp, "; %s }\n", decl.enqueue_c_namespace_end.c_str());
}
static void print_event_c_prototype_impl(FILE* fp)
{
fprintf(fp, "void %s(zeek::analyzer::Analyzer* analyzer%s",
decl.enqueue_c_fullname.c_str(),
args.size() ? ", " : "" );
print_event_c_prototype_args(fp);
fprintf(fp, ")");
fprintf(fp, "\n");
}
static void print_event_c_body(FILE* fp)
{
fprintf(fp, "\t{\n");
fprintf(fp, "\t// Note that it is intentional that here we do not\n");
fprintf(fp, "\t// check if %s is NULL, which should happen *before*\n",
decl.c_fullname.c_str());
fprintf(fp, "\t// %s is called to avoid unnecessary Val\n",
decl.enqueue_c_fullname.c_str());
fprintf(fp, "\t// allocation.\n");
fprintf(fp, "\n");
BuiltinFuncArg* connection_arg = nullptr;
fprintf(fp, "\tzeek::event_mgr.Enqueue(%s, zeek::Args{\n", decl.c_fullname.c_str());
for ( int i = 0; i < (int) args.size(); ++i )
{
fprintf(fp, "\t ");
args[i]->PrintValConstructor(fp);
fprintf(fp, ",\n");
if ( args[i]->Type() == TYPE_CONNECTION )
{
if ( connection_arg == nullptr )
connection_arg = args[i];
else
{
// We are seeing two connection type arguments.
yywarn("Warning: with more than connection-type "
"event arguments, bifcl only passes "
"the first one to EventMgr as cookie.");
}
}
}
fprintf(fp, "\t },\n\t zeek::util::detail::SOURCE_LOCAL, analyzer ? analyzer->GetID() : 0");
if ( connection_arg )
// Pass the connection to the EventMgr as the "cookie"
fprintf(fp, ", %s", connection_arg->Name());
fprintf(fp, ");\n");
fprintf(fp, "\t}\n\n");
//fprintf(fp, "%s // end namespace\n", decl.enqueue_c_namespace_end.c_str());
}
void record_bif_item(const char* id, const char* type)
{
if ( ! plugin )
return;
fprintf(fp_func_init, "\tplugin->AddBifItem(\"%s\", zeek::plugin::BifItem::%s);\n", id, type);
}
%}
%token TOK_LPP TOK_RPP TOK_LPB TOK_RPB TOK_LPPB TOK_RPPB TOK_VAR_ARG
%token TOK_BOOL
%token TOK_FUNCTION TOK_EVENT TOK_CONST TOK_ENUM TOK_OF
%token TOK_TYPE TOK_RECORD TOK_SET TOK_VECTOR TOK_OPAQUE TOK_TABLE TOK_MODULE
%token TOK_ARGS TOK_ARG TOK_ARGC
%token TOK_ID TOK_ATTR TOK_CSTR TOK_LF TOK_WS TOK_COMMENT
%token TOK_ATOM TOK_INT TOK_C_TOKEN
%left ',' ':'
%type <str> TOK_C_TOKEN TOK_ID TOK_CSTR TOK_WS TOK_COMMENT TOK_ATTR TOK_INT opt_ws type attr_list opt_attr_list opt_func_attrs
%type <val> TOK_ATOM TOK_BOOL
%union {
const char* str;
int val;
}
%%
builtin_lang: definitions
{
fprintf(fp_zeek_init, "} # end of export section\n");
fprintf(fp_zeek_init, "module %s;\n", GLOBAL_MODULE_NAME);
}
definitions: definitions definition opt_ws
{
if ( in_c_code )
fprintf(fp_func_def, "%s", $3);
else
fprintf(fp_zeek_init, "%s", $3);
}
| opt_ws
{
fprintf(fp_zeek_init, "export {\n");
fprintf(fp_zeek_init, "%s", $1);
}
;
definition: event_def
| func_def
| c_code_segment
| enum_def
| const_def
| type_def
| module_def
;
module_def: TOK_MODULE opt_ws TOK_ID opt_ws ';'
{
current_module = string($3);
fprintf(fp_zeek_init, "module %s;\n", $3);
}
// XXX: Add the netvar glue so that the event engine knows about
// the type. One still has to define the type in zeek.init.
// Would be nice, if we could just define the record type here
// and then copy to the .bif.zeek file, but type declarations in
// Zeek can be quite powerful. Don't know whether it's worth it
// extend the bif-language to be able to handle that all....
// Or we just support a simple form of record type definitions
// TODO: add other types (tables, sets)
type_def: TOK_TYPE opt_ws TOK_ID opt_ws ':' opt_ws type_def_types opt_ws ';'
{
set_decl_name($3);
fprintf(fp_netvar_h, "namespace zeek::%s { extern zeek::IntrusivePtr<zeek::%sType> %s; }\n",
decl.c_namespace_start.c_str(), type_name.c_str(), decl.bare_name.c_str());
fprintf(fp_netvar_def, "namespace zeek::%s { zeek::IntrusivePtr<zeek::%sType> %s; }\n",
decl.c_namespace_start.c_str(), type_name.c_str(), decl.bare_name.c_str());
fprintf(fp_netvar_def, "namespace %s { zeek::%sType * %s; }\n",
decl.c_namespace_start.c_str(), type_name.c_str(), decl.bare_name.c_str());
fprintf(fp_netvar_init,
"\tzeek::%s = zeek::id::find_type<zeek::%sType>(\"%s\");\n",
decl.c_fullname.c_str(), type_name.c_str(),
decl.zeek_fullname.c_str());
record_bif_item(decl.zeek_fullname.c_str(), "TYPE");
}
;
type_def_types: TOK_RECORD
{ set_definition_type(TYPE_DEF, "Record"); }
| TOK_SET
{ set_definition_type(TYPE_DEF, "Set"); }
| TOK_VECTOR
{ set_definition_type(TYPE_DEF, "Vector"); }
| TOK_TABLE
{ set_definition_type(TYPE_DEF, "Table"); }
;
opt_func_attrs: attr_list opt_ws
{ $$ = $1; }
| /* nothing */
{ $$ = ""; }
;
event_def: event_prefix opt_ws plain_head opt_func_attrs
{ fprintf(fp_zeek_init, "%s", $4); } end_of_head ';'
{
if ( events.find(decl.zeek_fullname) == events.end() )
{
print_event_c_prototype_header(fp_func_h);
print_event_c_prototype_impl(fp_func_def);
print_event_c_body(fp_func_def);
events.insert(decl.zeek_fullname);
}
}
func_def: func_prefix opt_ws typed_head opt_func_attrs
{ fprintf(fp_zeek_init, "%s", $4); } end_of_head body
;
enum_def: enum_def_1 enum_list TOK_RPB opt_attr_list
{
// First, put an end to the enum type decl.
fprintf(fp_zeek_init, "} ");
fprintf(fp_zeek_init, "%s", $4);
fprintf(fp_zeek_init, ";\n");
fprintf(fp_netvar_h, "}; }\n");
// Now generate the netvar's.
fprintf(fp_netvar_h, "namespace zeek::%s { extern zeek::IntrusivePtr<zeek::EnumType> %s; %s}\n",
decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str());
fprintf(fp_netvar_def, "namespace zeek::%s { zeek::IntrusivePtr<zeek::EnumType> %s; %s}\n",
decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str());
fprintf(fp_netvar_def, "namespace %s { zeek::EnumType * %s; %s }\n",
decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str());
fprintf(fp_netvar_init,
"\tzeek::%s = zeek::id::find_type<zeek::EnumType>(\"%s\");\n",
decl.c_fullname.c_str(), decl.zeek_fullname.c_str());
record_bif_item(decl.zeek_fullname.c_str(), "TYPE");
}
;
enum_def_1: TOK_ENUM opt_ws TOK_ID opt_ws TOK_LPB opt_ws
{
set_definition_type(TYPE_DEF, "Enum");
set_decl_name($3);
fprintf(fp_zeek_init, "type %s: enum %s{%s", decl.zeek_name.c_str(), $4, $6);
// this is the namespace were the enumerators are defined, not where
// the type is defined.
// We don't support fully qualified names as enumerators. Use a module name
fprintf(fp_netvar_h, "// NOLINTNEXTLINE(performance-enum-size)\n");
if ( decl.module_name != GLOBAL_MODULE_NAME )
fprintf(fp_netvar_h, "namespace BifEnum::%s { ", decl.module_name.c_str());
else
fprintf(fp_netvar_h, "namespace BifEnum { ");
fprintf(fp_netvar_h, "enum %s {\n", $3);
}
;
enum_list: enum_list TOK_ID opt_ws ',' opt_ws
{
fprintf(fp_zeek_init, "%s%s,%s", $2, $3, $5);
fprintf(fp_netvar_h, "\t%s,\n", $2);
}
| enum_list TOK_ID opt_ws '=' opt_ws TOK_INT opt_ws ',' opt_ws
{
fprintf(fp_zeek_init, "%s = %s%s,%s", $2, $6, $7, $9);
fprintf(fp_netvar_h, "\t%s = %s,\n", $2, $6);
}
| /* nothing */
;
const_def: TOK_CONST opt_ws TOK_ID opt_ws ':' opt_ws TOK_ID opt_ws ';'
{
set_definition_type(CONST_DEF, 0);
set_decl_name($3);
int typeidx = get_type_index($7);
char accessor[1024];
char accessor_smart[1024];
snprintf(accessor, sizeof(accessor), builtin_types[typeidx].accessor, "");
snprintf(accessor_smart, sizeof(accessor_smart), builtin_types[typeidx].accessor_smart, "");
fprintf(fp_netvar_h, "namespace zeek::%s { extern %s %s; }\n",
decl.c_namespace_start.c_str(),
builtin_types[typeidx].c_type_smart, decl.bare_name.c_str());
fprintf(fp_netvar_def, "namespace zeek::%s { %s %s; }\n",
decl.c_namespace_start.c_str(),
builtin_types[typeidx].c_type_smart, decl.bare_name.c_str());
fprintf(fp_netvar_def, "namespace %s { %s %s; } \n",
decl.c_namespace_start.c_str(),
builtin_types[typeidx].c_type, decl.bare_name.c_str());
if ( alternative_mode && ! plugin )
fprintf(fp_netvar_init, "\tzeek::detail::bif_initializers.emplace_back([]()\n");
fprintf(fp_netvar_init, "\t{\n");
fprintf(fp_netvar_init, "\tconst auto& v = zeek::id::find_const%s(\"%s\");\n",
builtin_types[typeidx].cast_smart, decl.zeek_fullname.c_str());
fprintf(fp_netvar_init, "\tzeek::%s = v%s;\n",
decl.c_fullname.c_str(), accessor_smart);
fprintf(fp_netvar_init, "\t}\n");
if ( alternative_mode && ! plugin )
fprintf(fp_netvar_init, "\t);\n");
record_bif_item(decl.zeek_fullname.c_str(), "CONSTANT");
}
attr_list:
attr_list TOK_ATTR
{ $$ = concat($1, $2); }
|
TOK_ATTR
;
opt_attr_list:
attr_list
| /* nothing */
{ $$ = ""; }
;
func_prefix: TOK_FUNCTION
{ set_definition_type(FUNC_DEF, 0); }
;
event_prefix: TOK_EVENT
{ set_definition_type(EVENT_DEF, 0); }
;
end_of_head: /* nothing */
{
fprintf(fp_zeek_init, ";\n");
}
;
typed_head: plain_head return_type
{
}
;
plain_head: head_1 args arg_end opt_ws
{
if ( var_arg )
fprintf(fp_zeek_init, "va_args: any");
else
{
for ( int i = 0; i < (int) args.size(); ++i )
{
if ( i > 0 )
fprintf(fp_zeek_init, ", ");
args[i]->PrintZeek(fp_zeek_init);
}
}
fprintf(fp_zeek_init, ")");
fprintf(fp_zeek_init, "%s", $4);
fprintf(fp_func_def, "%s", $4);
}
;
head_1: TOK_ID opt_ws arg_begin
{
const char* method_type = nullptr;
set_decl_name($1);
if ( definition_type == FUNC_DEF )
{
method_type = "function";
print_line_directive(fp_func_def);
}
else if ( definition_type == EVENT_DEF )
method_type = "event";
if ( method_type )
fprintf(fp_zeek_init,
"global %s: %s%s(",
decl.zeek_name.c_str(), method_type, $2);
if ( definition_type == FUNC_DEF )
{
fprintf(fp_func_init,
"\t(void) new zeek::detail::BuiltinFunc(zeek::%s_bif, \"%s\", false);\n",
decl.c_fullname.c_str(), decl.zeek_fullname.c_str());
// This is the "canonical" version, with argument type and order
// mostly for historical reasons. There's also no "zeek_" prefix
// in the function name itself, but does have a "_bif" suffix
// to potentially help differentiate from other functions
// (e.g. ones at global scope that may be used to implement
// the BIF itself).
fprintf(fp_func_h,
"namespace zeek::%s { extern zeek::ValPtr %s_bif(zeek::detail::Frame* frame, const zeek::Args*);%s }\n",
decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str());
fprintf(fp_func_def,
"zeek::ValPtr zeek::%s_bif(zeek::detail::Frame* frame, const zeek::Args* %s)",
decl.c_fullname.c_str(), arg_list_name);
record_bif_item(decl.zeek_fullname.c_str(), "FUNCTION");
}
else if ( definition_type == EVENT_DEF )
{
if ( events.find(decl.zeek_fullname) == events.end() )
{
// TODO: add namespace for events here
fprintf(fp_netvar_h,
"%sextern zeek::EventHandlerPtr %s; %s\n",
decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str());
fprintf(fp_netvar_def,
"%szeek::EventHandlerPtr %s; %s\n",
decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str());
fprintf(fp_netvar_init,
"\t%s = zeek::event_registry->Register(\"%s\");\n",
decl.c_fullname.c_str(), decl.zeek_fullname.c_str());
record_bif_item(decl.zeek_fullname.c_str(), "EVENT");
// C++ prototypes of zeek_event_* functions will
// be generated later.
}
}
}
;
arg_begin: TOK_LPP
{ args.clear(); var_arg = 0; }
;
arg_end: TOK_RPP
;
args: args_1
| opt_ws
{ /* empty, to avoid yacc complaint about type clash */ }
;
args_1: args_1 ',' opt_ws arg opt_ws opt_attr_list
{ if ( ! args.empty() ) args[args.size()-1]->SetAttrStr($6); }
| opt_ws arg opt_ws opt_attr_list
{ if ( ! args.empty() ) args[args.size()-1]->SetAttrStr($4); }
;
// TODO: Migrate all other compound types to this rule. Once the BiF language
// can parse all regular Zeek types, we can throw out the unnecessary
// boilerplate typedefs for addr_set, string_set, etc.
type:
TOK_OPAQUE opt_ws TOK_OF opt_ws TOK_ID
{ $$ = concat("opaque of ", $5); }
| TOK_ID
{ $$ = $1; }
;
arg: TOK_ID opt_ws ':' opt_ws type
{ args.push_back(new BuiltinFuncArg($1, $5)); }
| TOK_VAR_ARG
{
if ( definition_type == EVENT_DEF )
yyerror("events cannot have variable arguments");
var_arg = 1;
}
;
return_type: ':' opt_ws type opt_ws
{
BuiltinFuncArg* ret = new BuiltinFuncArg("", $3);
ret->PrintZeek(fp_zeek_init);
delete ret;
fprintf(fp_func_def, "%s", $4);
}
;
body: body_start c_body body_end
{
fprintf(fp_func_def, " // end of %s\n", decl.c_fullname.c_str());
print_line_directive(fp_func_def);
}
;
c_code_begin: /* empty */
{
in_c_code = true;
print_line_directive(fp_func_def);
}
;
c_code_end: /* empty */
{ in_c_code = false; }
;
body_start: TOK_LPB c_code_begin
{
int implicit_arg = 0;
int argc = args.size();
fprintf(fp_func_def, "{");
if ( argc > 0 || ! var_arg )
fprintf(fp_func_def, "\n");
if ( ! var_arg )
{
fprintf(fp_func_def, "\tif ( %s->size() != %d )\n", arg_list_name, argc);
fprintf(fp_func_def, "\t\t{\n");
fprintf(fp_func_def,
"\t\tzeek::emit_builtin_error(zeek::util::fmt(\"%s() takes exactly %d argument(s), got %%lu\", %s->size()));\n",
decl.zeek_fullname.c_str(), argc, arg_list_name);
fprintf(fp_func_def, "\t\treturn nullptr;\n");
fprintf(fp_func_def, "\t\t}\n");
}
else if ( argc > 0 )
{
fprintf(fp_func_def, "\tif ( %s->size() < %d )\n", arg_list_name, argc);
fprintf(fp_func_def, "\t\t{\n");
fprintf(fp_func_def,
"\t\tzeek::emit_builtin_error(zeek::util::fmt(\"%s() takes at least %d argument(s), got %%lu\", %s->size()));\n",
decl.zeek_fullname.c_str(), argc, arg_list_name);
fprintf(fp_func_def, "\t\treturn nullptr;\n");
fprintf(fp_func_def, "\t\t}\n");
}
for ( int i = 0; i < (int) args.size(); ++i )
args[i]->PrintCDef(fp_func_def, i + implicit_arg, var_arg);
print_line_directive(fp_func_def);
}
;
body_end: TOK_RPB c_code_end
{
fprintf(fp_func_def, "}");
}
;
c_code_segment: TOK_LPPB c_code_begin c_body c_code_end TOK_RPPB
;
c_body: opt_ws
{ fprintf(fp_func_def, "%s", $1); }
| c_body c_atom opt_ws
{ fprintf(fp_func_def, "%s", $3); }
;
c_atom: TOK_ID
{ fprintf(fp_func_def, "%s", $1); }
| TOK_C_TOKEN
{ fprintf(fp_func_def, "%s", $1); }
| TOK_ARG
{ fprintf(fp_func_def, "(*%s)", arg_list_name); }
| TOK_ARGS
{ fprintf(fp_func_def, "%s", arg_list_name); }
| TOK_ARGC
{ fprintf(fp_func_def, "%s->size()", arg_list_name); }
| TOK_CSTR
{ fprintf(fp_func_def, "%s", $1); }
| TOK_ATOM
{ fprintf(fp_func_def, "%c", $1); }
| TOK_INT
{ fprintf(fp_func_def, "%s", $1); }
;
opt_ws: opt_ws TOK_WS
{ $$ = concat($1, $2); }
| opt_ws TOK_LF
{ $$ = concat($1, "\n"); }
| opt_ws TOK_COMMENT
{
if ( in_c_code )
$$ = concat($1, $2);
else
if ( $2[1] == '#' )
// This is a special type of comment that is used to
// generate zeek script documentation, so pass it through.
$$ = concat($1, $2);
else
$$ = $1;
}
| /* empty */
{ $$ = ""; }
;
%%
extern char* yytext;
extern char* input_filename;
extern int line_number;
void err_exit(void);
void print_msg(const char msg[])
{
int msg_len = strlen(msg) + strlen(yytext) + 64;
char* msgbuf = new char[msg_len];
if ( yytext[0] == '\n' )
snprintf(msgbuf, msg_len, "%s, on previous line", msg);
else if ( yytext[0] == '\0' )
snprintf(msgbuf, msg_len, "%s, at end of file", msg);
else
snprintf(msgbuf, msg_len, "%s, at or near \"%s\"", msg, yytext);
/*
extern int column;
sprintf(msgbuf, "%*s\n%*s\n", column, "^", column, msg);
*/
if ( input_filename )
fprintf(stderr, "%s:%d: ", input_filename, line_number);
else
fprintf(stderr, "line %d: ", line_number);
fprintf(stderr, "%s\n", msgbuf);
delete [] msgbuf;
}
int yywarn(const char msg[])
{
print_msg(msg);
return 0;
}
int yyerror(const char msg[])
{
print_msg(msg);
err_exit();
return 0;
}

View file

@ -1,38 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <cstdint>
#include <cstdio>
enum builtin_func_arg_type : uint8_t {
#define DEFINE_BIF_TYPE(id, bif_type, bro_type, c_type, c_type_smart, accessor, accessor_smart, cast_smart, \
constructor, ctor_smart) \
id,
#include "bif_type.def"
#undef DEFINE_BIF_TYPE
};
extern const char* builtin_func_arg_type_bro_name[];
class BuiltinFuncArg final {
public:
BuiltinFuncArg(const char* arg_name, int arg_type);
BuiltinFuncArg(const char* arg_name, const char* arg_type_str, const char* arg_attr_str = "");
void SetAttrStr(const char* arg_attr_str) { attr_str = arg_attr_str; };
const char* Name() const { return name; }
int Type() const { return type; }
void PrintZeek(FILE* fp);
void PrintCDef(FILE* fp, int n, bool runtime_type_check = false);
void PrintCArg(FILE* fp, int n);
void PrintValConstructor(FILE* fp);
private:
const char* name;
int type;
const char* type_str;
const char* attr_str;
};

View file

@ -1,17 +0,0 @@
// (id, bif_type, zeek_type, c_type, c_type_smart, accessor, accessor_smart, cast_smart, constructor, ctor_smart)
DEFINE_BIF_TYPE(TYPE_ADDR, "addr", "addr", "zeek::AddrVal*", "zeek::IntrusivePtr<zeek::AddrVal>", "%s->AsAddrVal()", "%s", "<zeek::AddrVal>", "zeek::IntrusivePtr{zeek::AdoptRef{}, %s}", "std::move(%s)")
DEFINE_BIF_TYPE(TYPE_ANY, "any", "any", "zeek::Val*", "zeek::IntrusivePtr<zeek::Val>", "%s", "%s", "", "zeek::IntrusivePtr{zeek::AdoptRef{}, %s}", "std::move(%s)")
DEFINE_BIF_TYPE(TYPE_BOOL, "bool", "bool", "int", "int", "%s->AsBool()", "%s->AsBool()", "", "zeek::val_mgr->Bool(%s)", "zeek::val_mgr->Bool(%s)")
DEFINE_BIF_TYPE(TYPE_CONN_ID, "conn_id", "conn_id", "zeek::Val*", "zeek::IntrusivePtr<zeek::Val>", "%s", "%s", "", "zeek::IntrusivePtr{zeek::AdoptRef{}, %s}", "std::move(%s)")
DEFINE_BIF_TYPE(TYPE_CONNECTION, "connection", "connection", "zeek::Connection*", "zeek::Connection*", "%s->AsRecordVal()->GetOrigin()", "%s->AsRecordVal()->GetOrigin()", "", "%s->GetVal()", "%s->GetVal()")
DEFINE_BIF_TYPE(TYPE_COUNT, "count", "count", "zeek_uint_t", "zeek_uint_t", "%s->AsCount()", "%s->AsCount()", "", "zeek::val_mgr->Count(%s)", "zeek::val_mgr->Count(%s)")
DEFINE_BIF_TYPE(TYPE_DOUBLE, "double", "double", "double", "double", "%s->AsDouble()", "%s->AsDouble()", "", "zeek::make_intrusive<zeek::DoubleVal>(%s)", "zeek::make_intrusive<zeek::DoubleVal>(%s)")
DEFINE_BIF_TYPE(TYPE_FILE, "file", "file", "zeek::File*", "zeek::IntrusivePtr<zeek::FileVal>", "%s->AsFile()", "%s", "", "zeek::make_intrusive<zeek::FileVal>(zeek::IntrusivePtr{zeek::AdoptRef{}, %s})", "std::move(%s)")
DEFINE_BIF_TYPE(TYPE_INT, "int", "int", "zeek_int_t", "zeek_int_t", "%s->AsInt()", "%s->AsInt()", "", "zeek::val_mgr->Int(%s)", "zeek::val_mgr->Int(%s)")
DEFINE_BIF_TYPE(TYPE_INTERVAL, "interval", "interval", "double", "double", "%s->AsInterval()", "%s->AsInterval()", "", "zeek::make_intrusive<zeek::IntervalVal>(%s, Seconds)", "zeek::make_intrusive<zeek::IntervalVal>(%s, Seconds)")
DEFINE_BIF_TYPE(TYPE_PATTERN, "pattern", "pattern", "RE_Matcher*", "zeek::IntrusivePtr<zeek::PatternVal>", "%s->AsPattern()", "%s", "<zeek::PatternVal>", "zeek::make_intrusive<zeek::PatternVal>(%s)", "std::move(%s)")
DEFINE_BIF_TYPE(TYPE_PORT, "port", "port", "zeek::PortVal*", "zeek::IntrusivePtr<zeek::PortVal>", "%s->AsPortVal()", "%s", "<zeek::PortVal>", "zeek::IntrusivePtr{zeek::AdoptRef{}, %s}", "std::move(%s)")
DEFINE_BIF_TYPE(TYPE_STRING, "string", "string", "zeek::StringVal*", "zeek::IntrusivePtr<zeek::StringVal>", "%s->AsStringVal()", "%s", "<zeek::StringVal>", "zeek::IntrusivePtr{zeek::AdoptRef{}, %s}", "std::move(%s)")
DEFINE_BIF_TYPE(TYPE_SUBNET, "subnet", "subnet", "zeek::SubNetVal*", "zeek::IntrusivePtr<zeek::SubNetVal>", "%s->AsSubNetVal()", "%s", "<zeek::SubNetVal>", "zeek::IntrusivePtr{zeek::AdoptRef{}, %s}", "std::move(%s)")
DEFINE_BIF_TYPE(TYPE_TIME, "time", "time", "double", "double", "%s->AsTime()", "%s->AsTime()", "", "zeek::make_intrusive<zeek::TimeVal>(%s)", "zeek::make_intrusive<zeek::TimeVal>(%s)")
DEFINE_BIF_TYPE(TYPE_OTHER, "", "", "zeek::Val*", "zeek::IntrusivePtr<zeek::Val>", "%s", "%s", "", "zeek::IntrusivePtr{zeek::AdoptRef{}, %s}", "std::move(%s)")

View file

@ -1,19 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
//
// These functions are used by both Zeek and bifcl.
//
#pragma once
#include <string>
static constexpr const char* GLOBAL_MODULE_NAME = "GLOBAL";
extern std::string extract_module_name(const char* name);
extern std::string extract_var_name(const char* name);
extern std::string normalized_module_name(const char* module_name); // w/o ::
// Concatenates module_name::var_name unless var_name is already fully
// qualified, in which case it is returned unmodified.
extern std::string make_full_var_name(const char* module_name, const char* var_name);

View file

@ -1,59 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "module_util.h"
#include <cstring>
#include <string>
using namespace std;
static int streq(const char* s1, const char* s2) { return ! strcmp(s1, s2); }
// Returns it without trailing "::".
string extract_module_name(const char* name) {
string module_name = name;
string::size_type pos = module_name.rfind("::");
if ( pos == string::npos )
return GLOBAL_MODULE_NAME;
module_name.erase(pos);
return module_name;
}
string extract_var_name(const char* name) {
string var_name = name;
string::size_type pos = var_name.rfind("::");
if ( pos == string::npos )
return var_name;
if ( pos + 2 > var_name.size() )
return "";
return var_name.substr(pos + 2);
}
string normalized_module_name(const char* module_name) {
size_t mod_len;
if ( mod_len = strlen(module_name); mod_len >= 2 && streq(module_name + mod_len - 2, "::") )
mod_len -= 2;
return {module_name, mod_len};
}
string make_full_var_name(const char* module_name, const char* var_name) {
if ( ! module_name || streq(module_name, GLOBAL_MODULE_NAME) || strstr(var_name, "::") ) {
if ( streq(GLOBAL_MODULE_NAME, extract_module_name(var_name).c_str()) )
return extract_var_name(var_name);
return var_name;
}
string full_name = normalized_module_name(module_name);
full_name += "::";
full_name += var_name;
return full_name;
}

View file

@ -1,5 +0,0 @@
# ##############################################################################
# Recurse on sub-directories
add_subdirectory(lib)
add_subdirectory(src)

File diff suppressed because it is too large Load diff

View file

@ -1,34 +0,0 @@
Big features
* Variable context (xid, call in RPC)? -- no variable context
* Helpers
* Connection states and actions
* Case and analyzer redef
* &also withinput
* Explicit analyzer context (interface + instantiation) "withcontext"
+ Interface with C++ and Zeek (events, extern, weird)
+ Incremental input
+ ASCII protocols
+ Reassembly
- Dealing with exceptions
- Dependency analysis to save parsing time on unused fields
- Performance measurement
Small features
* Restructure the code: break up pac.{h,cc}
* ref counting (to keep certain structures)
* analyzer context as a parameter of class
* &autolength
* find a better name for "analyzer_context" ("analcxt", "context", "analyzer") $context
* &if
* &autolength (now &restofdata)
* Use vector<> instead of array<>?
* set end_of_data when &length = ...
- make the `default' case mandatory?
- &inline
- &warn and &check? (follow &if)
- typedef?
Binpac 1
- create a namespace for each .pac file
- type equivalence
- byteorder() for every type?

View file

@ -1,44 +0,0 @@
include(TestBigEndian)
test_big_endian(HOST_BIGENDIAN)
include(CheckTypeSize)
check_type_size("unsigned int" SIZEOF_UNSIGNED_INT)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/binpac.h.in ${CMAKE_CURRENT_BINARY_DIR}/binpac.h)
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
set(binpac_headers ${CMAKE_CURRENT_BINARY_DIR}/binpac.h binpac_analyzer.h binpac_buffer.h
binpac_bytestring.h binpac_exception.h binpac_regex.h)
set(binpac_lib_SRCS binpac_buffer.cc binpac_bytestring.cc binpac_regex.cc)
if (BUILD_STATIC_BINPAC)
add_library(binpac_static STATIC)
target_sources(binpac_static PRIVATE ${binpac_lib_SRCS})
set_target_properties(binpac_static PROPERTIES OUTPUT_NAME binpac)
install(TARGETS binpac_static DESTINATION ${CMAKE_INSTALL_LIBDIR})
if (MSVC)
target_compile_options(binpac_static PRIVATE "/J")
endif ()
set(BinPAC_LIBRARY binpac_static CACHE STRING "BinPAC library" FORCE)
else ()
add_library(binpac_lib SHARED)
target_sources(binpac_lib PRIVATE ${binpac_lib_SRCS})
target_sources(binpac_lib INTERFACE ${binpac_headers})
set_target_properties(binpac_lib PROPERTIES MACOSX_RPATH true OUTPUT_NAME binpac)
if (MSVC)
target_compile_options(binpac_lib PRIVATE "/J")
endif ()
install(TARGETS binpac_lib DESTINATION ${CMAKE_INSTALL_LIBDIR})
set(BinPAC_LIBRARY binpac_lib CACHE STRING "BinPAC library" FORCE)
endif ()
if (ZEEK_ROOT_DIR)
# Installed in binpac subdir just for organization purposes.
install(FILES ${binpac_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/binpac)
else ()
install(FILES ${binpac_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
endif ()
set(BinPAC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
CACHE STRING "BinPAC header directories" FORCE)

View file

@ -1,3 +0,0 @@
This directory contains a library needed by generated C++ code from
binpac. Note that the library is not needed by the binpac compiler
itself.

View file

@ -1,160 +0,0 @@
// Do not edit binpac.h, edit binpac.h.in instead!
#ifndef binpac_h
#define binpac_h
#ifndef _MSC_VER
#include <sys/param.h>
#endif
#cmakedefine HOST_BIGENDIAN
#ifdef HOST_BIGENDIAN
#define HOST_BYTEORDER bigendian
#else
#define HOST_BYTEORDER littleendian
#endif
#include <cassert>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <string>
// Expose C99 functionality from inttypes.h, which would otherwise not be
// available in C++.
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
static constexpr void BINPAC_ASSERT(bool val) { assert(val); }
using namespace std;
namespace binpac {
const int bigendian = 0;
const int littleendian = 1;
const int unspecified_byteorder = -1;
#ifndef pac_type_defs
#define pac_type_defs
using int8 = int8_t;
using int16 = int16_t;
using int32 = int32_t;
using int64 = int64_t;
using uint8 = uint8_t;
using uint16 = uint16_t;
using uint32 = uint32_t;
using uint64 = uint64_t;
using nulptr = void*;
using voidptr = void*;
using byteptr = uint8*;
using const_byteptr = const uint8*;
using const_charptr = const char*;
static_assert(sizeof(unsigned int) == 4, "Unexpected size of unsigned int");
#endif /* pac_type_defs */
/* Handling byte order */
namespace {
inline uint16 pac_swap(const uint16 x) { return (x >> 8) | ((x & 0xff) << 8); }
inline int16 pac_swap(const int16 x) {
// Forward to unsigned version with argument/result casted
// appropriately.
uint16 (*p)(const uint16) = &pac_swap;
return (*p)(x);
}
inline uint32 pac_swap(const uint32 x) {
return (x >> 24) | ((x & 0xff0000) >> 8) | ((x & 0xff00) << 8) | ((x & 0xff) << 24);
}
inline int32 pac_swap(const int32 x) {
// Forward to unsigned version with argument/result casted
// appropriately.
uint32 (*p)(const uint32) = &pac_swap;
return (*p)(x);
}
inline uint64 pac_swap(const uint64 x) {
return x >> 56 | (x & 0xff000000000000) >> 40 | (x & 0xff0000000000) >> 24 | (x & 0xff00000000) >> 8 |
(x & 0xff000000) << 8 | (x & 0xff0000) << 24 | (x & 0xff00) << 40 | (x & 0xff) << 56;
}
inline int64 pac_swap(const int64 x) {
// Forward to unsigned version with argument/result casted
// appropriately.
uint64 (*p)(const uint64) = &pac_swap;
return (*p)(x);
}
template<typename T>
static constexpr T FixByteOrder(int byteorder, T x) {
if ( byteorder == HOST_BYTEORDER )
return x;
return static_cast<T>(pac_swap(x));
}
template<class T>
inline T UnMarshall(const unsigned char* data, int byteorder) {
T result = 0;
for ( int i = 0; i < (int)sizeof(T); ++i )
result = (result << 8) | data[byteorder == bigendian ? i : sizeof(T) - 1 - i];
return result;
}
inline const char* do_fmt(const char* format, va_list ap) {
static char buf[1024];
vsnprintf(buf, sizeof(buf), format, ap);
return buf;
}
inline string strfmt(const char* format, ...) {
va_list ap;
va_start(ap, format);
const char* r = do_fmt(format, ap);
va_end(ap);
return {r};
}
} // anonymous namespace
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define binpac_fmt(...) strfmt(__VA_ARGS__).c_str()
class RefCount {
public:
RefCount() { count = 1; }
virtual ~RefCount() {}
void Ref() { ++count; }
int Unref() {
BINPAC_ASSERT(count > 0);
return --count;
}
private:
int count;
};
namespace {
inline void Unref(RefCount* x) {
if ( x && x->Unref() <= 0 )
delete x;
}
} // anonymous namespace
} // namespace binpac
#include "binpac_analyzer.h"
#include "binpac_buffer.h"
#include "binpac_bytestring.h"
#include "binpac_exception.h"
#include "binpac_regex.h"
#endif /* binpac_h */

View file

@ -1,26 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef binpac_an_h
#define binpac_an_h
namespace binpac {
// TODO: Add the Done() function
// The interface for a connection analyzer
class ConnectionAnalyzer {
public:
virtual ~ConnectionAnalyzer() = default;
virtual void NewData(bool is_orig, const unsigned char* begin_of_data, const unsigned char* end_of_data) = 0;
};
// The interface for a flow analyzer
class FlowAnalyzer {
public:
virtual ~FlowAnalyzer() = default;
virtual void NewData(const unsigned char* begin_of_data, const unsigned char* end_of_data) = 0;
};
} // namespace binpac
#endif // binpac_an_h

View file

@ -1,459 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // for memcpy
#define binpac_regex_h
#include "binpac.h"
#include "binpac_buffer.h"
namespace binpac {
extern double network_time();
namespace {
const unsigned char CR = '\r';
const unsigned char LF = '\n';
} // namespace
binpac::FlowBuffer::Policy binpac::FlowBuffer::policy = {
// max_capacity
10 * 1024 * 1024,
// min_capacity
512,
// contract_threshold
2 * 1024 * 1024,
};
FlowBuffer::FlowBuffer(LineBreakStyle linebreak_style) {
buffer_length_ = 0;
buffer_ = nullptr;
orig_data_begin_ = nullptr;
orig_data_end_ = nullptr;
linebreak_style_ = linebreak_style;
linebreak_style_default = linebreak_style;
linebreaker_ = 0;
ResetLineState();
mode_ = UNKNOWN_MODE;
frame_length_ = 0;
chunked_ = false;
data_seq_at_orig_data_end_ = 0;
eof_ = false;
have_pending_request_ = false;
buffer_n_ = 0;
NewMessage();
}
FlowBuffer::~FlowBuffer() {
if ( buffer_ )
free(buffer_);
}
void FlowBuffer::NewMessage() {
BINPAC_ASSERT(frame_length_ >= 0);
int bytes_to_advance = 0;
if ( buffer_n_ == 0 ) {
switch ( mode_ ) {
case LINE_MODE: bytes_to_advance = (frame_length_ + (linebreak_style_ == STRICT_CRLF ? 2 : 1)); break;
case FRAME_MODE: bytes_to_advance = frame_length_; break;
case UNKNOWN_MODE: break;
}
}
orig_data_begin_ += bytes_to_advance;
BINPAC_ASSERT(orig_data_begin_ <= orig_data_end_);
buffer_n_ = 0;
message_complete_ = false;
ContractBuffer();
}
void FlowBuffer::ResetLineState() {
switch ( linebreak_style_ ) {
case CR_OR_LF: state_ = CR_OR_LF_0; break;
case STRICT_CRLF: state_ = STRICT_CRLF_0; break;
case LINE_BREAKER: break; // Nothing to reset
default: BINPAC_ASSERT(0); break;
}
}
void FlowBuffer::ExpandBuffer(int length) {
if ( buffer_length_ >= length )
return;
if ( length < policy.min_capacity )
length = policy.min_capacity;
if ( length < buffer_length_ * 2 )
length = buffer_length_ * 2;
if ( length > policy.max_capacity ) {
std::string reason = strfmt("expand past max capacity %d/%d", length, policy.max_capacity);
throw ExceptionFlowBufferAlloc(reason.c_str());
}
// Allocate a new buffer and copy the existing contents
buffer_length_ = length;
unsigned char* new_buf = (unsigned char*)realloc(buffer_, buffer_length_);
if ( ! new_buf )
throw ExceptionFlowBufferAlloc("expand realloc OOM");
buffer_ = new_buf;
}
void FlowBuffer::ContractBuffer() {
if ( buffer_length_ < policy.contract_threshold )
return;
buffer_length_ = policy.min_capacity;
unsigned char* new_buf = (unsigned char*)realloc(buffer_, buffer_length_);
if ( ! new_buf )
throw ExceptionFlowBufferAlloc("contract realloc OOM");
buffer_ = new_buf;
}
void FlowBuffer::SetLineBreaker(unsigned char* lbreaker) {
linebreaker_ = *lbreaker;
linebreak_style_default = linebreak_style_;
linebreak_style_ = LINE_BREAKER;
}
void FlowBuffer::UnsetLineBreaker() { linebreak_style_ = linebreak_style_default; }
void FlowBuffer::NewLine() {
FlowBuffer::NewMessage();
mode_ = LINE_MODE;
frame_length_ = 0;
chunked_ = false;
have_pending_request_ = true;
if ( state_ == FRAME_0 )
ResetLineState();
MarkOrCopyLine();
}
void FlowBuffer::NewFrame(int frame_length, bool chunked) {
FlowBuffer::NewMessage();
mode_ = FRAME_MODE;
frame_length_ = frame_length;
chunked_ = chunked;
have_pending_request_ = true;
MarkOrCopyFrame();
}
void FlowBuffer::BufferData(const_byteptr data, const_byteptr end) {
mode_ = FRAME_MODE;
frame_length_ += (end - data);
MarkOrCopyFrame();
NewData(data, end);
}
void FlowBuffer::FinishBuffer() { message_complete_ = true; }
void FlowBuffer::GrowFrame(int length) {
BINPAC_ASSERT(frame_length_ >= 0);
if ( length <= frame_length_ )
return;
BINPAC_ASSERT(! chunked_ || frame_length_ == 0);
mode_ = FRAME_MODE;
frame_length_ = length;
MarkOrCopyFrame();
}
void FlowBuffer::DiscardData() {
mode_ = UNKNOWN_MODE;
message_complete_ = false;
have_pending_request_ = false;
orig_data_begin_ = orig_data_end_ = nullptr;
buffer_n_ = 0;
frame_length_ = 0;
ContractBuffer();
}
void FlowBuffer::set_eof() {
// fprintf(stderr, "EOF\n");
eof_ = true;
if ( chunked_ )
frame_length_ = orig_data_end_ - orig_data_begin_;
if ( frame_length_ < 0 )
frame_length_ = 0;
}
void FlowBuffer::NewData(const_byteptr begin, const_byteptr end) {
BINPAC_ASSERT(begin <= end);
ClearPreviousData();
BINPAC_ASSERT((buffer_n_ == 0 && message_complete_) || orig_data_begin_ == orig_data_end_);
orig_data_begin_ = begin;
orig_data_end_ = end;
data_seq_at_orig_data_end_ += (end - begin);
MarkOrCopy();
}
void FlowBuffer::MarkOrCopy() {
if ( ! message_complete_ ) {
switch ( mode_ ) {
case LINE_MODE: MarkOrCopyLine(); break;
case FRAME_MODE: MarkOrCopyFrame(); break;
default: break;
}
}
}
void FlowBuffer::ClearPreviousData() {
// All previous data must have been processed or buffered already
if ( orig_data_begin_ < orig_data_end_ ) {
BINPAC_ASSERT(buffer_n_ == 0);
if ( chunked_ ) {
if ( frame_length_ > 0 ) {
frame_length_ -= (orig_data_end_ - orig_data_begin_);
}
orig_data_begin_ = orig_data_end_;
}
}
}
void FlowBuffer::NewGap(int length) {
ClearPreviousData();
if ( chunked_ && frame_length_ >= 0 ) {
frame_length_ -= length;
if ( frame_length_ < 0 )
frame_length_ = 0;
}
orig_data_begin_ = orig_data_end_ = nullptr;
MarkOrCopy();
}
void FlowBuffer::MarkOrCopyLine() {
switch ( linebreak_style_ ) {
case CR_OR_LF: MarkOrCopyLine_CR_OR_LF(); break;
case STRICT_CRLF: MarkOrCopyLine_STRICT_CRLF(); break;
case LINE_BREAKER: MarkOrCopyLine_LINEBREAK(); break;
default: BINPAC_ASSERT(0); break;
}
}
/*
Finite state automaton for CR_OR_LF:
(!--line is complete, *--add to buffer)
CR_OR_LF_0:
CR: CR_OR_LF_1 !
LF: CR_OR_LF_0 !
.: CR_OR_LF_0 *
CR_OR_LF_1:
CR: CR_OR_LF_1 !
LF: CR_OR_LF_0
.: CR_OR_LF_0 *
*/
void FlowBuffer::MarkOrCopyLine_CR_OR_LF() {
if ( ! (orig_data_begin_ && orig_data_end_) )
return;
if ( state_ == CR_OR_LF_1 && orig_data_begin_ < orig_data_end_ && *orig_data_begin_ == LF ) {
state_ = CR_OR_LF_0;
++orig_data_begin_;
}
const_byteptr data;
for ( data = orig_data_begin_; data < orig_data_end_; ++data ) {
switch ( *data ) {
case CR: state_ = CR_OR_LF_1; goto found_end_of_line;
case LF:
// state_ = CR_OR_LF_0;
goto found_end_of_line;
default:
// state_ = CR_OR_LF_0;
break;
}
}
AppendToBuffer(orig_data_begin_, orig_data_end_ - orig_data_begin_);
return;
found_end_of_line:
if ( buffer_n_ == 0 ) {
frame_length_ = data - orig_data_begin_;
}
else {
AppendToBuffer(orig_data_begin_, data + 1 - orig_data_begin_);
// But eliminate the last CR or LF
--buffer_n_;
}
message_complete_ = true;
#if DEBUG_FLOW_BUFFER
fprintf(stderr, "%.6f Line complete: [%s]\n", network_time(),
string((const char*)begin(), (const char*)end()).c_str());
#endif
}
/*
Finite state automaton and STRICT_CRLF:
(!--line is complete, *--add to buffer)
STRICT_CRLF_0:
CR: STRICT_CRLF_1 *
LF: STRICT_CRLF_0 *
.: STRICT_CRLF_0 *
STRICT_CRLF_1:
CR: STRICT_CRLF_1 *
LF: STRICT_CRLF_0 ! (--buffer_n_)
.: STRICT_CRLF_0 *
*/
void FlowBuffer::MarkOrCopyLine_STRICT_CRLF() {
const_byteptr data;
for ( data = orig_data_begin_; data < orig_data_end_; ++data ) {
switch ( *data ) {
case CR: state_ = STRICT_CRLF_1; break;
case LF:
if ( state_ == STRICT_CRLF_1 ) {
state_ = STRICT_CRLF_0;
goto found_end_of_line;
}
break;
default: state_ = STRICT_CRLF_0; break;
}
}
AppendToBuffer(orig_data_begin_, orig_data_end_ - orig_data_begin_);
return;
found_end_of_line:
if ( buffer_n_ == 0 ) {
frame_length_ = data - 1 - orig_data_begin_;
}
else {
AppendToBuffer(orig_data_begin_, data + 1 - orig_data_begin_);
// Pop the preceding CR and LF from the buffer
buffer_n_ -= 2;
}
message_complete_ = true;
#if DEBUG_FLOW_BUFFER
fprintf(stderr, "%.6f Line complete: [%s]\n", network_time(),
string((const char*)begin(), (const char*)end()).c_str());
#endif
}
void FlowBuffer::MarkOrCopyLine_LINEBREAK() {
if ( ! (orig_data_begin_ && orig_data_end_) )
return;
const_byteptr data;
for ( data = orig_data_begin_; data < orig_data_end_; ++data ) {
if ( *data == linebreaker_ )
goto found_end_of_line;
}
AppendToBuffer(orig_data_begin_, orig_data_end_ - orig_data_begin_);
return;
found_end_of_line:
if ( buffer_n_ == 0 ) {
frame_length_ = data - orig_data_begin_;
}
else {
AppendToBuffer(orig_data_begin_, data + 1 - orig_data_begin_);
// But eliminate the last 'linebreaker' character
--buffer_n_;
}
message_complete_ = true;
#if DEBUG_FLOW_BUFFER
fprintf(stderr, "%.6f Line complete: [%s]\n", network_time(),
string((const char*)begin(), (const char*)end()).c_str());
#endif
}
// Invariants:
//
// When buffer_n_ == 0:
// Frame = [orig_data_begin_..(orig_data_begin_ + frame_length_)]
//
// When buffer_n_ > 0:
// Frame = [0..buffer_n_][orig_data_begin_..]
void FlowBuffer::MarkOrCopyFrame() {
if ( mode_ == FRAME_MODE && state_ == CR_OR_LF_1 && orig_data_begin_ < orig_data_end_ ) {
// Skip the lingering LF
if ( *orig_data_begin_ == LF ) {
++orig_data_begin_;
}
state_ = FRAME_0;
}
if ( buffer_n_ == 0 ) {
// If there is enough data
if ( frame_length_ >= 0 && orig_data_end_ - orig_data_begin_ >= frame_length_ ) {
// Do nothing except setting the message complete flag
message_complete_ = true;
}
else {
if ( ! chunked_ ) {
AppendToBuffer(orig_data_begin_, orig_data_end_ - orig_data_begin_);
}
message_complete_ = false;
}
}
else {
BINPAC_ASSERT(! chunked_);
int bytes_to_copy = orig_data_end_ - orig_data_begin_;
message_complete_ = false;
if ( frame_length_ >= 0 && buffer_n_ + bytes_to_copy >= frame_length_ ) {
bytes_to_copy = frame_length_ - buffer_n_;
message_complete_ = true;
}
AppendToBuffer(orig_data_begin_, bytes_to_copy);
}
#if DEBUG_FLOW_BUFFER
if ( message_complete_ ) {
fprintf(stderr, "%.6f frame complete: [%s]\n", network_time(),
string((const char*)begin(), (const char*)end()).c_str());
}
#endif
}
void FlowBuffer::AppendToBuffer(const_byteptr data, int len) {
if ( len <= 0 )
return;
BINPAC_ASSERT(! chunked_);
ExpandBuffer(buffer_n_ + len);
memcpy(buffer_ + buffer_n_, data, len);
buffer_n_ += len;
orig_data_begin_ += len;
BINPAC_ASSERT(orig_data_begin_ <= orig_data_end_);
}
} // namespace binpac

View file

@ -1,170 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef binpac_buffer_h
#define binpac_buffer_h
#include <sys/types.h>
#include "binpac.h"
namespace binpac {
class FlowBuffer {
public:
struct Policy {
int max_capacity;
int min_capacity;
int contract_threshold;
};
enum LineBreakStyle : uint8_t {
CR_OR_LF, // CR or LF or CRLF
STRICT_CRLF, // CR followed by LF
CR_LF_NUL, // CR or LF or CR-LF or CR-NUL
LINE_BREAKER, // User specified linebreaker
};
FlowBuffer(LineBreakStyle linebreak_style = CR_OR_LF);
virtual ~FlowBuffer();
void NewData(const_byteptr begin, const_byteptr end);
void NewGap(int length);
// Interface for delayed parsing. Sometimes BinPAC doesn't get the
// buffering right and then one can use these to feed parts
// individually and assemble them internally. After calling
// FinishBuffer(), one can send the upper-layer flow an FlowEOF() to
// trigger parsing.
void BufferData(const_byteptr data, const_byteptr end);
void FinishBuffer();
// Discard unprocessed data
void DiscardData();
// Whether there is enough data for the frame
bool ready() const { return message_complete_ || mode_ == UNKNOWN_MODE; }
inline const_byteptr begin() const {
BINPAC_ASSERT(ready());
return (buffer_n_ == 0) ? orig_data_begin_ : buffer_;
}
inline const_byteptr end() const {
BINPAC_ASSERT(ready());
if ( buffer_n_ == 0 ) {
BINPAC_ASSERT(frame_length_ >= 0);
const_byteptr end = orig_data_begin_ + frame_length_;
BINPAC_ASSERT(end <= orig_data_end_);
return end;
}
else
return buffer_ + buffer_n_;
}
inline int data_length() const {
if ( buffer_n_ > 0 )
return buffer_n_;
if ( frame_length_ < 0 || orig_data_begin_ + frame_length_ > orig_data_end_ )
return orig_data_end_ - orig_data_begin_;
else
return frame_length_;
}
inline bool data_available() const { return buffer_n_ > 0 || orig_data_end_ > orig_data_begin_; }
void SetLineBreaker(unsigned char* lbreaker);
void UnsetLineBreaker();
void NewLine();
// A negative frame_length represents a frame till EOF
void NewFrame(int frame_length, bool chunked_);
void GrowFrame(int new_frame_length);
int data_seq() const {
int data_seq_at_orig_data_begin = data_seq_at_orig_data_end_ - (orig_data_end_ - orig_data_begin_);
if ( buffer_n_ > 0 )
return data_seq_at_orig_data_begin;
else
return data_seq_at_orig_data_begin + data_length();
}
bool eof() const { return eof_; }
void set_eof();
bool have_pending_request() const { return have_pending_request_; }
static void init(Policy p) { policy = p; }
protected:
// Reset the buffer for a new message
void NewMessage();
void ClearPreviousData();
// Expand the buffer to at least <length> bytes. If there
// are contents in the existing buffer, copy them to the new
// buffer.
void ExpandBuffer(int length);
// Contract the buffer to some minimum capacity.
// Existing contents in the buffer are preserved (but only usage
// at the time of creation this function is when the contents
// are being discarded due to parsing exception or have already been
// copied out after parsing a complete unit).
void ContractBuffer();
// Reset line state when transit from frame mode to line mode.
void ResetLineState();
void AppendToBuffer(const_byteptr data, int len);
// MarkOrCopy{Line,Frame} sets message_complete_ and
// marks begin/end pointers if a line/frame is complete,
// otherwise it clears message_complete_ and copies all
// the original data to the buffer.
//
void MarkOrCopy();
void MarkOrCopyLine();
void MarkOrCopyFrame();
void MarkOrCopyLine_CR_OR_LF();
void MarkOrCopyLine_STRICT_CRLF();
void MarkOrCopyLine_LINEBREAK();
int buffer_n_; // number of bytes in the buffer
int buffer_length_; // size of the buffer
unsigned char* buffer_;
bool message_complete_;
int frame_length_;
bool chunked_;
const_byteptr orig_data_begin_, orig_data_end_;
LineBreakStyle linebreak_style_;
LineBreakStyle linebreak_style_default;
unsigned char linebreaker_;
enum : uint8_t {
UNKNOWN_MODE,
LINE_MODE,
FRAME_MODE,
} mode_;
enum : uint8_t {
CR_OR_LF_0,
CR_OR_LF_1,
STRICT_CRLF_0,
STRICT_CRLF_1,
FRAME_0,
} state_;
int data_seq_at_orig_data_end_;
bool eof_;
bool have_pending_request_;
static Policy policy;
};
using flow_buffer_t = FlowBuffer*;
} // namespace binpac
#endif // binpac_buffer_h

View file

@ -1,17 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#define binpac_regex_h
#include "binpac_bytestring.h"
#include <stdlib.h>
namespace binpac {
std::string std_string(bytestring const* s) { return std::string((const char*)s->begin(), (const char*)s->end()); }
int bytestring_to_int(bytestring const* s) { return atoi((const char*)s->begin()); }
double bytestring_to_double(bytestring const* s) { return atof((const char*)s->begin()); }
} // namespace binpac

View file

@ -1,145 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef binpac_bytestring_h
#define binpac_bytestring_h
#include <cstring>
#include <string>
#include "binpac.h"
namespace binpac {
template<class T>
class datastring;
template<class T>
class const_datastring {
public:
const_datastring() : begin_(nullptr), end_(nullptr) {}
const_datastring(T const* data, int length) : begin_(data), end_(data + length) {}
const_datastring(const T* begin, const T* end) : begin_(begin), end_(end) {}
const_datastring(datastring<T> const& s) : begin_(s.begin()), end_(s.end()) {}
void init(const T* data, int length) {
begin_ = data;
end_ = data + length;
}
T const* begin() const { return begin_; }
T const* end() const { return end_; }
int length() const { return end_ - begin_; }
T const& operator[](int index) const { return begin()[index]; }
bool operator==(const_datastring<T> const& s) {
if ( length() != s.length() )
return false;
return memcmp((const void*)begin(), (const void*)s.begin(), sizeof(T) * length()) == 0;
}
void set_begin(T const* begin) { begin_ = begin; }
void set_end(T const* end) { end_ = end; }
private:
T const* begin_;
T const* end_;
};
using const_bytestring = const_datastring<uint8>;
template<class T>
class datastring {
public:
datastring() { clear(); }
datastring(T* data, int len) { set(data, len); }
datastring(T const* begin, T const* end) { set_const(begin, end - begin); }
datastring(datastring<T> const& x) : data_(x.data()), length_(x.length()) {}
explicit datastring(const_datastring<T> const& x) { set_const(x.begin(), x.length()); }
datastring const& operator=(datastring<T> const& x) {
if ( this == &x )
return *this;
BINPAC_ASSERT(! data_);
set(x.data(), x.length());
return *this;
}
void init(T const* begin, int length) {
BINPAC_ASSERT(! data_);
set_const(begin, length);
}
void clear() {
data_ = nullptr;
length_ = 0;
}
void free() {
if ( data_ )
delete[] data_;
clear();
}
void clone() { set_const(begin(), length()); }
datastring const& operator=(const_datastring<T> const& x) {
BINPAC_ASSERT(! data_);
set_const(x.begin(), x.length());
return *this;
}
T const& operator[](int index) const { return begin()[index]; }
T* data() const { return data_; }
int length() const { return length_; }
T const* begin() const { return data_; }
T const* end() const { return data_ + length_; }
private:
void set(T* data, int len) {
data_ = data;
length_ = len;
}
void set_const(T const* data, int len) {
length_ = len;
data_ = new T[len + 1];
memcpy(data_, data, sizeof(T) * len);
data_[len] = 0;
}
T* data_;
int length_;
};
using bytestring = datastring<uint8>;
inline const char* c_str(bytestring const& s) { return (const char*)s.begin(); }
inline std::string std_str(const_bytestring const& s) { return {(const char*)s.begin(), (const char*)s.end()}; }
inline bool operator==(bytestring const& s1, const char* s2) { return strcmp(c_str(s1), s2) == 0; }
inline void get_pointers(const_bytestring const& s, uint8 const** pbegin, uint8 const** pend) {
*pbegin = s.begin();
*pend = s.end();
}
inline void get_pointers(bytestring const* s, uint8 const** pbegin, uint8 const** pend) {
*pbegin = s->begin();
*pend = s->end();
}
} // namespace binpac
#endif // binpac_bytestring_h

View file

@ -1,98 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef binpac_exception_h
#define binpac_exception_h
#include <cinttypes>
#include <cstdint>
#include <string>
namespace binpac {
class Exception {
public:
Exception(const char* m = nullptr) : msg_("binpac exception: ") {
if ( m )
append(m);
// abort();
}
void append(std::string m) { msg_ += m; }
std::string msg() const { return msg_; }
const char* c_msg() const { return msg_.c_str(); }
protected:
std::string msg_;
};
class ExceptionEnforceViolation : public Exception {
public:
ExceptionEnforceViolation(const char* where) { append(binpac_fmt("&enforce violation : %s", where)); }
};
class ExceptionOutOfBound : public Exception {
public:
ExceptionOutOfBound(const char* where, int len_needed, int len_given) {
append(binpac_fmt("out_of_bound: %s: %d > %d", where, len_needed, len_given));
}
};
class ExceptionInvalidCase : public Exception {
public:
ExceptionInvalidCase(const char* location, int64_t index, const char* expected)
: location_(location), index_(index), expected_(expected) {
append(binpac_fmt("invalid case: %s: %" PRIi64 " (%s)", location, index, expected));
}
protected:
const char* location_;
int64_t index_;
std::string expected_;
};
class ExceptionInvalidCaseIndex : public Exception {
public:
ExceptionInvalidCaseIndex(const char* location, int64_t index) : location_(location), index_(index) {
append(binpac_fmt("invalid index for case: %s: %" PRIi64, location, index));
}
protected:
const char* location_;
int64_t index_;
};
class ExceptionInvalidOffset : public Exception {
public:
ExceptionInvalidOffset(const char* location, int min_offset, int offset)
: location_(location), min_offset_(min_offset), offset_(offset) {
append(binpac_fmt("invalid offset: %s: min_offset = %d, offset = %d", location, min_offset, offset));
}
protected:
const char* location_;
int min_offset_, offset_;
};
class ExceptionStringMismatch : public Exception {
public:
ExceptionStringMismatch(const char* location, const char* expected, const char* actual_data) {
append(binpac_fmt("string mismatch at %s: \nexpected pattern: \"%s\"\nactual data: \"%s\"", location, expected,
actual_data));
}
};
class ExceptionInvalidStringLength : public Exception {
public:
ExceptionInvalidStringLength(const char* location, int len) {
append(binpac_fmt("invalid length string: %s: %d", location, len));
}
};
class ExceptionFlowBufferAlloc : public Exception {
public:
ExceptionFlowBufferAlloc(const char* reason) { append(binpac_fmt("flowbuffer allocation failed: %s", reason)); }
};
} // namespace binpac
#endif // binpac_exception_h

View file

@ -1,13 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include <vector>
namespace zeek {
class RE_Matcher;
}
namespace binpac {
std::vector<zeek::RE_Matcher*>* uncompiled_re_matchers = nullptr;
}

View file

@ -1,76 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef binpac_regex_h
#define binpac_regex_h
#include <vector>
#include "zeek/RE.h"
#include "binpac.h"
namespace zeek {
class RE_Matcher;
}
namespace binpac {
// Must be called before any binpac functionality is used.
//
// Note, this must be declared/defined here, and inline, because the RE
// functionality can only be used when compiling from inside Zeek.
// A copy is made of any FlowBuffer policy struct data passed.
inline void init(FlowBuffer::Policy* fbp = nullptr);
// Internal vector recording not yet compiled matchers.
extern std::vector<zeek::RE_Matcher*>* uncompiled_re_matchers;
class RegExMatcher {
public:
RegExMatcher(const char* pattern) : pattern_(pattern) {
if ( ! uncompiled_re_matchers )
uncompiled_re_matchers = new std::vector<zeek::RE_Matcher*>;
re_matcher_ = new zeek::RE_Matcher(pattern_.c_str());
uncompiled_re_matchers->push_back(re_matcher_);
}
~RegExMatcher() { delete re_matcher_; }
// Returns the length of longest match, or -1 on mismatch.
int MatchPrefix(const_byteptr data, int len) { return re_matcher_->MatchPrefix(data, len); }
private:
friend void ::binpac::init(FlowBuffer::Policy*);
// Function, and state, for compiling matchers.
static void init();
string pattern_;
zeek::RE_Matcher* re_matcher_;
};
inline void RegExMatcher::init() {
if ( ! uncompiled_re_matchers )
return;
for ( const auto& matcher : *uncompiled_re_matchers ) {
if ( ! matcher->Compile() ) {
fprintf(stderr, "binpac: cannot compile regular expression\n");
exit(1);
}
}
uncompiled_re_matchers->clear();
}
inline void init(FlowBuffer::Policy* fbp) {
RegExMatcher::init();
if ( fbp )
FlowBuffer::init(*fbp);
}
} // namespace binpac
#endif // binpac_regex_h

View file

@ -1,2 +0,0 @@
Note: It's unclear which of these patches have in fact already been
applied. We should figure that out ...

View file

@ -1,66 +0,0 @@
diff -urN bro-1.2.1-orig/src/pac_paramtype.cc bro-1.2.1-ssl-binpac/src/pac_paramtype.cc
--- bro-1.2.1-orig/src/pac_paramtype.cc 2006-07-26 15:02:40.000000000 -0700
+++ bro-1.2.1-ssl-binpac/src/pac_paramtype.cc 2007-05-10 15:09:47.470104000 -0700
@@ -208,7 +208,13 @@
const char *parse_func;
string parse_params;
- if ( ref_type->incremental_input() )
+ if ( buffer_mode() == BUFFER_NOTHING )
+ {
+ ASSERT(!ref_type->incremental_input());
+ parse_func = kParseFuncWithoutBuffer;
+ parse_params = "0, 0";
+ }
+ else if ( ref_type->incremental_input() )
{
parse_func = kParseFuncWithBuffer;
parse_params = env->RValue(flow_buffer_id);
@@ -239,15 +245,24 @@
if ( incremental_input() )
{
- ASSERT(parsing_complete_var());
- out_cc->println("%s = %s;",
- env->LValue(parsing_complete_var()),
- call_parse_func.c_str());
-
- // parsing_complete_var might have been already
- // evaluated when set to false
- if ( ! env->Evaluated(parsing_complete_var()) )
- env->SetEvaluated(parsing_complete_var());
+ if ( buffer_mode() == BUFFER_NOTHING )
+ {
+ out_cc->println("%s;", call_parse_func.c_str());
+ out_cc->println("%s = true;",
+ env->LValue(parsing_complete_var()));
+ }
+ else
+ {
+ ASSERT(parsing_complete_var());
+ out_cc->println("%s = %s;",
+ env->LValue(parsing_complete_var()),
+ call_parse_func.c_str());
+
+ // parsing_complete_var might have been already
+ // evaluated when set to false
+ if ( ! env->Evaluated(parsing_complete_var()) )
+ env->SetEvaluated(parsing_complete_var());
+ }
}
else
{
diff -urN bro-1.2.1-orig/src/pac_type.cc bro-1.2.1-ssl-binpac/src/pac_type.cc
--- bro-1.2.1-orig/src/pac_type.cc 2006-07-26 15:02:40.000000000 -0700
+++ bro-1.2.1-ssl-binpac/src/pac_type.cc 2007-05-24 10:56:42.140658000 -0700
@@ -501,8 +501,8 @@
if ( buffer_mode() == BUFFER_NOTHING )
{
- out_cc->println("%s = true;",
- env->LValue(parsing_complete_var()));
+ // this is the empty type
+ DoGenParseCode(out_cc, env, data, flags);
}
else if ( buffer_input() )
{

View file

@ -1,21 +0,0 @@
diff -urN bro-1.2.1-orig/src/pac_type.cc bro-1.2.1-ssl-binpac/src/pac_type.cc
--- bro-1.2.1-orig/src/pac_type.cc 2006-07-26 15:02:40.000000000 -0700
+++ bro-1.2.1-ssl-binpac/src/pac_type.cc 2007-05-24 10:56:42.140658000 -0700
@@ -393,7 +393,7 @@
break;
case BUFFER_BY_LENGTH:
- if ( buffering_state_var_field_ )
+ if ( env->GetDataType(buffering_state_id) )
{
out_cc->println("if ( %s == 0 )",
env->RValue(buffering_state_id));
@@ -421,7 +421,7 @@
frame_buffer_arg.c_str(),
attr_chunked() ? "true" : "false");
- if ( buffering_state_var_field_ )
+ if ( env->GetDataType(buffering_state_id) )
{
out_cc->println("%s = 1;",
env->LValue(buffering_state_id));

View file

@ -1,87 +0,0 @@
binpac fixes
----------------
numbers of issues below correspond to the patch numbers
(1) correct calculation of minimal header size in pac_expr.cc
- problem: EXPR_CALLARGS and EXPR_CASE not considered for the calculation
of minimal header size
- solution: added two cases in switch stmt of Expr::MinimalHeaderSize
for EXPR_CALLARGS and EXPR_CASE
(2) ensure parsing of fields first referenced in a case expression or
let field with an &if attribute
- problem: in cases where the if expression evaluates to false or the
proper case does not occur, fields get not parsed at all
- solution: force evaluation of all IDs referenced in a let field with
if attribute or a case expression before the body of the corresponding
switch stmt or the if stmt
- added public method Expr::ForceIDEval, properly called before
generating the code of a field with if attribute or the case expression
(3) properly assert the use of fields with an if attribute
- problem: the use of fields with an if attribute was not asserted in all
cases and asserted in the wrong way in some others due to the
corresponding BINPAC_ASSERT only called upon parsing the field
- solution: perform BINPAC_ASSERT upon calling the fields accessor
function
- moved BINPAC_ASSERT statement from LetField::GenEval to
Type::GenPubDecls
(4) incremental input with records with a non-negative StaticSize
- problem: incremental input with records with a StaticSize >= 0
cannot be performed due to necessary length attribute, leading to
an invalid call of GenBoundaryCheck in RecordType::DoGenParseCode
- solution: added a check for incremental input in
RecordType::DoGenParseCode before calling GenBoundaryCheck
(5) empty type with incremental input
- problem: with an empty type and incremental input, although the
Parse function is created, it is never called, leading to problems,
if additional actions are to be performed when encountering that
empty type
- solution: generate call to Parse of empty type in Type::GenParseBuffer
(6) parsing loop in flow ParseBuffer (while(true))
- problem: while(true) leads to problems after parsing of a type is
complete; at this time, it is unexpected that parsing continues, even
if no data is available in the flow buffer
- solution: check if data is available before starting a new parsing
cycle
- added a method data_available to FlowBuffer
- changed while(true) in FlowDecl::GenCodeFlowUnit to
while(flow_buffer_->data_available())
(7) initialization of flow buffer in CaseType with bufferable fields
in cases
- problem: initialization of buffer occurs in every Parse call,
regardless if it was initialized before or not; initialization
is correct only on first such occurrence
- solution: check to buffer_state is to be created always when
buffering_state_id is in environment in Type::GenBufferConfig
- changed condition from buffering_state_var_field_ to
env->GetDataType(buffering_state_id)
(8) allowing init and cleanup code to be redefined, as well as addition
of code to FlowEOF calls in analyzer and flow
- problem 1: when refining an analyzer or flow definition, additional
init and cleanup code was not allowed, if these were already defined
before; this leads to problems when adding new members, as these
cannot be initialized and destroyed properly
- solution: allow init and cleanup code to be specified more than once
- changed deifnitions and usage of constructor_helper and
destructor_helper to allow for lists of constructor and destructor
helpers (similar to member declarations) in pac_analyzer.h and
pac_analyzer.cc
- problem 2: in some cases, it is desirable to execute code when
encountering the end of the input stream, which is not possible in
binpac
- solution: added a %eof binpac primitive similar to %init, which adds
code to the FlowEOF function of an analyzer or a flow

View file

@ -1,77 +0,0 @@
find_package(FLEX REQUIRED)
find_package(BISON REQUIRED)
bison_target(PACParser pac_parse.yy ${CMAKE_CURRENT_BINARY_DIR}/pac_parse.cc
DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/pac_parse.h COMPILE_FLAGS "--debug")
flex_target(PACScanner pac_scan.ll ${CMAKE_CURRENT_BINARY_DIR}/pac_scan.cc)
add_flex_bison_dependency(PACScanner PACParser)
if (MSVC)
set_property(SOURCE pac_scan.cc APPEND_STRING PROPERTY COMPILE_FLAGS "/wd4018")
else ()
set_property(SOURCE pac_scan.cc APPEND_STRING PROPERTY COMPILE_FLAGS "-Wno-sign-compare")
endif ()
set(binpac_SRCS
${BISON_PACParser_INPUT}
${FLEX_PACScanner_INPUT}
${BISON_PACParser_OUTPUTS}
${FLEX_PACScanner_OUTPUTS}
pac_action.cc
pac_analyzer.cc
pac_array.cc
pac_attr.cc
pac_btype.cc
pac_case.cc
pac_conn.cc
pac_context.cc
pac_cstr.cc
pac_datadep.cc
pac_dataptr.cc
pac_dataunit.cc
pac_decl.cc
pac_embedded.cc
pac_enum.cc
pac_expr.cc
pac_exttype.cc
pac_field.cc
pac_flow.cc
pac_func.cc
pac_id.cc
pac_inputbuf.cc
pac_let.cc
pac_param.cc
pac_paramtype.cc
pac_primitive.cc
pac_record.cc
pac_redef.cc
pac_regex.cc
pac_state.cc
pac_strtype.cc
pac_type.cc
pac_typedecl.cc
pac_withinput.cc
pac_output.cc
pac_utils.cc
pac_exception.cc
pac_main.cc)
add_executable(binpac ${binpac_SRCS})
target_include_directories(binpac BEFORE PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_include_directories(binpac BEFORE PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
if (MSVC)
target_compile_options(binpac PUBLIC "/J")
# If building separately from zeek, we need to add the libunistd subdirectory
# so that linking doesn't fail.
if ("${CMAKE_PROJECT_NAME}" STREQUAL "BinPAC")
add_subdirectory(${PROJECT_SOURCE_DIR}auxil/libunistd EXCLUDE_FROM_ALL)
endif ()
target_link_libraries(binpac PRIVATE libunistd)
endif ()
install(TARGETS binpac DESTINATION bin)
# This is set to assist superprojects that want to build BinPac from source and
# rely on it as a target
set(BinPAC_EXE binpac CACHE STRING "BinPAC executable" FORCE)

View file

@ -1,81 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_action.h"
#include "pac_embedded.h"
#include "pac_exception.h"
#include "pac_id.h"
#include "pac_output.h"
#include "pac_type.h"
#include "pac_typedecl.h"
#include "pac_utils.h"
AnalyzerAction::AnalyzerAction(ID* action_id, When when, ActionParam* param, EmbeddedCode* code)
: AnalyzerElement(ACTION), action_id_(action_id), when_(when), param_(param), code_(code), analyzer_(nullptr) {}
AnalyzerAction::~AnalyzerAction() {
delete action_id_;
delete param_;
delete code_;
}
string AnalyzerAction::action_function() const { return strfmt("Action_%s", action_id_->Name()); }
void AnalyzerAction::InstallHook(AnalyzerDecl* analyzer) {
ASSERT(0);
analyzer_ = analyzer;
// param_->MainDataType()->InstallAction(this);
}
void AnalyzerAction::GenCode(Output* out_h, Output* out_cc, AnalyzerDecl* decl) {
Env action_func_env(decl->env(), this);
action_func_env.AddID(param_->id(), TEMP_VAR, param_->DataType());
action_func_env.SetEvaluated(param_->id());
string action_func_proto = strfmt("%s(%s)", action_function().c_str(), ParamDecls(&action_func_env).c_str());
out_h->println("void %s;", action_func_proto.c_str());
out_cc->println("void %s::%s {", decl->class_name().c_str(), action_func_proto.c_str());
out_cc->inc_indent();
code_->GenCode(out_cc, &action_func_env);
out_cc->println("");
out_cc->dec_indent();
out_cc->println("}");
out_cc->println("");
}
string AnalyzerAction::ParamDecls(Env* env) const { return param_->DeclStr(env); }
Type* ActionParam::MainDataType() const {
// Note: this is not equal to DataType()
Type* main_type = TypeDecl::LookUpType(type()->type_id());
if ( ! main_type ) {
throw Exception(type()->type_id(), "type not defined");
}
return main_type;
}
Type* ActionParam::DataType() const {
Type* main_type = MainDataType();
if ( ! type()->field_id() ) {
return main_type;
}
else {
Type* member_type = main_type->MemberDataType(type()->field_id());
if ( ! member_type ) {
throw Exception(type()->field_id(), strfmt("cannot find member type for `%s.%s'", type()->type_id()->Name(),
type()->field_id()->Name()));
}
return member_type;
}
}
string ActionParam::DeclStr(Env* env) const {
return strfmt("%s %s", DataType()->DataTypeStr().c_str(), env->LValue(id()));
}

View file

@ -1,68 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_action_h
#define pac_action_h
// Classes representing analyzer actions.
#include "pac_analyzer.h"
#include "pac_common.h"
class AnalyzerAction : public AnalyzerElement {
public:
enum When { BEFORE, AFTER };
AnalyzerAction(ID* action_id, When when, ActionParam* param, EmbeddedCode* code);
~AnalyzerAction() override;
When when() const { return when_; }
ActionParam* param() const { return param_; }
AnalyzerDecl* analyzer() const { return analyzer_; }
string action_function() const;
// Generate function prototype and code for the action
void GenCode(Output* out_h, Output* out_cc, AnalyzerDecl* decl);
// Install the hook at the corresponding data type parsing
// function to invoke the action.
void InstallHook(AnalyzerDecl* analyzer);
private:
string ParamDecls(Env* env) const;
ID* action_id_;
When when_;
ActionParam* param_;
EmbeddedCode* code_;
AnalyzerDecl* analyzer_;
};
class ActionParam {
public:
ActionParam(const ID* id, ActionParamType* type) : id_(id), type_(type) {}
const ID* id() const { return id_; }
ActionParamType* type() const { return type_; }
Type* MainDataType() const;
Type* DataType() const;
string DeclStr(Env* env) const;
private:
const ID* id_;
ActionParamType* type_;
};
class ActionParamType {
public:
ActionParamType(const ID* type_id, const ID* field_id = 0) : type_id_(type_id), field_id_(field_id) {}
const ID* type_id() const { return type_id_; }
const ID* field_id() const { return field_id_; }
protected:
const ID *type_id_, *field_id_;
};
#endif // pac_action_h

View file

@ -1,265 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_analyzer.h"
#include "pac_action.h"
#include "pac_context.h"
#include "pac_embedded.h"
#include "pac_exception.h"
#include "pac_expr.h"
#include "pac_flow.h"
#include "pac_func.h"
#include "pac_output.h"
#include "pac_param.h"
#include "pac_paramtype.h"
#include "pac_state.h"
#include "pac_type.h"
#include "pac_varfield.h"
AnalyzerDecl::AnalyzerDecl(ID* id, DeclType decl_type, ParamList* params) : TypeDecl(id, params, new DummyType()) {
decl_type_ = decl_type;
statevars_ = new StateVarList();
actions_ = new AnalyzerActionList();
helpers_ = new AnalyzerHelperList();
functions_ = new FunctionList();
constructor_helpers_ = new AnalyzerHelperList();
destructor_helpers_ = new AnalyzerHelperList();
eof_helpers_ = new AnalyzerHelperList();
SetAnalyzerContext();
env_ = nullptr;
}
AnalyzerDecl::~AnalyzerDecl() {
delete_list(StateVarList, statevars_);
delete_list(AnalyzerActionList, actions_);
delete_list(AnalyzerHelperList, helpers_);
delete_list(FunctionList, functions_);
delete_list(ParamList, params_);
delete_list(AnalyzerHelperList, constructor_helpers_);
delete_list(AnalyzerHelperList, destructor_helpers_);
delete_list(AnalyzerHelperList, eof_helpers_);
}
void AnalyzerDecl::AddElements(AnalyzerElementList* elemlist) {
ASSERT(! env_);
foreach (i, AnalyzerElementList, elemlist) {
AnalyzerElement* elem = *i;
switch ( elem->type() ) {
case AnalyzerElement::STATE: {
ASSERT(0);
AnalyzerState* state_elem = (AnalyzerState*)elem;
statevars_->insert(statevars_->end(), state_elem->statevars()->begin(), state_elem->statevars()->end());
} break;
case AnalyzerElement::ACTION: {
ASSERT(0);
AnalyzerAction* action_elem = (AnalyzerAction*)elem;
actions_->push_back(action_elem);
} break;
case AnalyzerElement::HELPER: {
AnalyzerHelper* helper_elem = (AnalyzerHelper*)elem;
switch ( helper_elem->helper_type() ) {
case AnalyzerHelper::INIT_CODE: constructor_helpers_->push_back(helper_elem); break;
case AnalyzerHelper::CLEANUP_CODE: destructor_helpers_->push_back(helper_elem); break;
case AnalyzerHelper::EOF_CODE: eof_helpers_->push_back(helper_elem); break;
default: helpers_->push_back(helper_elem);
}
} break;
case AnalyzerElement::FUNCTION: {
AnalyzerFunction* func_elem = (AnalyzerFunction*)elem;
Function* func = func_elem->function();
func->set_analyzer_decl(this);
functions_->push_back(func);
} break;
case AnalyzerElement::FLOW: {
AnalyzerFlow* flow_elem = (AnalyzerFlow*)elem;
ProcessFlowElement(flow_elem);
} break;
case AnalyzerElement::DATAUNIT: {
AnalyzerDataUnit* dataunit_elem = (AnalyzerDataUnit*)elem;
ProcessDataUnitElement(dataunit_elem);
} break;
}
}
}
string AnalyzerDecl::class_name() const { return id_->Name(); }
void AnalyzerDecl::Prepare() {
TypeDecl::Prepare();
ASSERT(statevars_->empty());
ASSERT(actions_->empty());
foreach (i, FunctionList, functions_) {
Function* function = *i;
function->Prepare(env_);
}
foreach (i, StateVarList, statevars_) {
StateVar* statevar = *i;
env_->AddID(statevar->id(), STATE_VAR, statevar->type());
}
foreach (i, AnalyzerActionList, actions_) {
AnalyzerAction* action = *i;
action->InstallHook(this);
}
}
void AnalyzerDecl::GenForwardDeclaration(Output* out_h) {
out_h->println("class %s;", class_name().c_str());
foreach (i, FunctionList, functions_) {
Function* function = *i;
function->GenForwardDeclaration(out_h);
}
}
void AnalyzerDecl::GenActions(Output* out_h, Output* out_cc) {
foreach (i, AnalyzerActionList, actions_) {
(*i)->GenCode(out_h, out_cc, this);
}
}
void AnalyzerDecl::GenHelpers(Output* out_h, Output* out_cc) {
foreach (i, AnalyzerHelperList, helpers_) {
(*i)->GenCode(out_h, out_cc, this);
}
}
void AnalyzerDecl::GenPubDecls(Output* out_h, Output* out_cc) {
TypeDecl::GenPubDecls(out_h, out_cc);
GenProcessFunc(out_h, out_cc);
GenGapFunc(out_h, out_cc);
GenEOFFunc(out_h, out_cc);
out_h->println("");
if ( ! functions_->empty() ) {
out_h->println("// Functions");
GenFunctions(out_h, out_cc);
out_h->println("");
}
// TODO: export public state variables
}
void AnalyzerDecl::GenPrivDecls(Output* out_h, Output* out_cc) {
TypeDecl::GenPrivDecls(out_h, out_cc);
if ( ! helpers_->empty() ) {
out_h->println("");
out_h->println("// Additional members");
GenHelpers(out_h, out_cc);
}
// TODO: declare state variables
}
void AnalyzerDecl::GenInitCode(Output* out_cc) {
TypeDecl::GenInitCode(out_cc);
foreach (i, AnalyzerHelperList, constructor_helpers_) {
(*i)->GenCode(nullptr, out_cc, this);
}
}
void AnalyzerDecl::GenCleanUpCode(Output* out_cc) {
TypeDecl::GenCleanUpCode(out_cc);
foreach (i, AnalyzerHelperList, destructor_helpers_) {
(*i)->GenCode(nullptr, out_cc, this);
}
}
void AnalyzerDecl::GenStateVarDecls(Output* out_h) {
foreach (i, StateVarList, statevars_) {
StateVar* var = *i;
var->GenDecl(out_h, env_);
}
}
void AnalyzerDecl::GenStateVarSetFunctions(Output* out_h) {
foreach (i, StateVarList, statevars_) {
StateVar* var = *i;
var->GenSetFunction(out_h, env_);
}
}
void AnalyzerDecl::GenStateVarInitCode(Output* out_cc) {
foreach (i, StateVarList, statevars_) {
StateVar* var = *i;
var->GenInitCode(out_cc, env_);
}
}
void AnalyzerDecl::GenStateVarCleanUpCode(Output* out_cc) {
foreach (i, StateVarList, statevars_) {
StateVar* var = *i;
var->GenCleanUpCode(out_cc, env_);
}
}
void AnalyzerDecl::GenFunctions(Output* out_h, Output* out_cc) {
foreach (i, FunctionList, functions_) {
Function* function = *i;
function->GenCode(out_h, out_cc);
}
}
AnalyzerState::~AnalyzerState() {
// Note: do not delete elements of statevars_, because they
// are referenced by the AnalyzerDecl.
delete statevars_;
}
AnalyzerHelper::~AnalyzerHelper() { delete code_; }
void AnalyzerHelper::GenCode(Output* out_h, Output* out_cc, AnalyzerDecl* decl) {
Output* out = nullptr;
switch ( helper_type_ ) {
case MEMBER_DECLS: out = out_h; break;
case INIT_CODE:
case CLEANUP_CODE:
case EOF_CODE: out = out_cc; break;
}
ASSERT(out);
code()->GenCode(out, decl->env());
}
FlowField::FlowField(ID* flow_id, ParameterizedType* flow_type)
: Field(FLOW_FIELD, TYPE_NOT_TO_BE_PARSED | CLASS_MEMBER | PUBLIC_READABLE, flow_id, flow_type) {}
void FlowField::GenInitCode(Output* out_cc, Env* env) { type_->GenPreParsing(out_cc, env); }
AnalyzerFlow::AnalyzerFlow(Direction dir, ID* type_id, ExprList* params)
: AnalyzerElement(FLOW), dir_(dir), type_id_(type_id) {
if ( ! params )
params = new ExprList();
// Add "this" to the list of params
params->insert(params->begin(), new Expr(this_id->clone()));
ID* flow_id = ((dir == UP) ? upflow_id : downflow_id)->clone();
ParameterizedType* flow_type = new ParameterizedType(type_id_, params);
flow_field_ = new FlowField(flow_id, flow_type);
flow_decl_ = nullptr;
}
AnalyzerFlow::~AnalyzerFlow() { delete flow_field_; }
FlowDecl* AnalyzerFlow::flow_decl() {
DEBUG_MSG("Getting flow_decl for %s\n", type_id_->Name());
if ( ! flow_decl_ ) {
Decl* decl = Decl::LookUpDecl(type_id_);
if ( decl && decl->decl_type() == Decl::FLOW )
flow_decl_ = static_cast<FlowDecl*>(decl);
if ( ! flow_decl_ ) {
throw Exception(this, "cannot find the flow declaration");
}
}
return flow_decl_;
}

View file

@ -1,159 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_analyzer_h
#define pac_analyzer_h
#include "pac_common.h"
#include "pac_field.h"
#include "pac_typedecl.h"
class AnalyzerElement;
class AnalyzerState;
class AnalyzerAction; // defined in pac_action.h
class AnalyzerHelper;
class AnalyzerFlow;
class AnalyzerDataUnit;
class AnalyzerFunction;
class ConnDecl;
class FlowDecl;
typedef vector<AnalyzerHelper*> AnalyzerHelperList;
typedef vector<Function*> FunctionList;
class AnalyzerDecl : public TypeDecl {
public:
AnalyzerDecl(ID* id, DeclType decl_type, ParamList* params);
~AnalyzerDecl() override;
void AddElements(AnalyzerElementList* elemlist);
void Prepare() override;
void GenForwardDeclaration(Output* out_h) override;
// void GenCode(Output *out_h, Output *out_cc);
void GenInitCode(Output* out_cc) override;
void GenCleanUpCode(Output* out_cc) override;
string class_name() const;
// string cookie_name() const;
protected:
virtual void ProcessFlowElement(AnalyzerFlow* flow_elem) = 0;
virtual void ProcessDataUnitElement(AnalyzerDataUnit* dataunit_elem) = 0;
// Generate public/private declarations for member functions and
// variables
void GenPubDecls(Output* out_h, Output* out_cc) override;
void GenPrivDecls(Output* out_h, Output* out_cc) override;
// Generate the NewData() function
virtual void GenProcessFunc(Output* out_h, Output* out_cc) = 0;
// Generate the NewGap() function
virtual void GenGapFunc(Output* out_h, Output* out_cc) = 0;
// Generate the FlowEOF() function
virtual void GenEOFFunc(Output* out_h, Output* out_cc) = 0;
// Generate the functions
void GenFunctions(Output* out_h, Output* out_cc);
// Generate the action functions
void GenActions(Output* out_h, Output* out_cc);
// Generate the helper code segments
void GenHelpers(Output* out_h, Output* out_cc);
// Generate declarations for state variables and their set functions
void GenStateVarDecls(Output* out_h);
void GenStateVarSetFunctions(Output* out_h);
// Generate code for initializing and cleaning up (including
// memory de-allocating) state variables
void GenStateVarInitCode(Output* out_cc);
void GenStateVarCleanUpCode(Output* out_cc);
StateVarList* statevars_;
AnalyzerActionList* actions_;
AnalyzerHelperList* helpers_;
FunctionList* functions_;
AnalyzerHelperList* constructor_helpers_;
AnalyzerHelperList* destructor_helpers_;
AnalyzerHelperList* eof_helpers_;
};
class AnalyzerElement : public Object {
public:
enum ElementType { STATE, ACTION, FUNCTION, HELPER, FLOW, DATAUNIT };
AnalyzerElement(ElementType type) : type_(type) {}
virtual ~AnalyzerElement() {}
ElementType type() const { return type_; }
private:
ElementType type_;
};
// A collection of variables representing analyzer states.
class AnalyzerState : public AnalyzerElement {
public:
AnalyzerState(StateVarList* statevars) : AnalyzerElement(STATE), statevars_(statevars) {}
~AnalyzerState() override;
StateVarList* statevars() const { return statevars_; }
private:
StateVarList* statevars_;
};
// A collection of embedded C++ code
class AnalyzerHelper : public AnalyzerElement {
public:
enum Type {
MEMBER_DECLS,
INIT_CODE,
CLEANUP_CODE,
EOF_CODE,
};
AnalyzerHelper(Type helper_type, EmbeddedCode* code)
: AnalyzerElement(HELPER), helper_type_(helper_type), code_(code) {}
~AnalyzerHelper() override;
Type helper_type() const { return helper_type_; }
void GenCode(Output* out_h, Output* out_cc, AnalyzerDecl* decl);
EmbeddedCode* code() const { return code_; }
private:
Type helper_type_;
EmbeddedCode* code_;
};
// The type and parameters of (uni-directional) flows of a connection.
class FlowField : public Field {
public:
FlowField(ID* flow_id, ParameterizedType* flow_type);
void GenInitCode(Output* out, Env* env) override;
};
class AnalyzerFlow : public AnalyzerElement {
public:
enum Direction { UP, DOWN };
AnalyzerFlow(Direction dir, ID* type_id, ExprList* params);
~AnalyzerFlow() override;
Direction dir() const { return dir_; }
FlowField* flow_field() const { return flow_field_; }
FlowDecl* flow_decl();
private:
Direction dir_;
ID* type_id_;
FlowField* flow_field_;
FlowDecl* flow_decl_;
};
#endif // pac_analyzer_h

View file

@ -1,595 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_array.h"
#include "pac_attr.h"
#include "pac_dataptr.h"
#include "pac_exception.h"
#include "pac_expr.h"
#include "pac_exttype.h"
#include "pac_id.h"
#include "pac_number.h"
#include "pac_output.h"
#include "pac_utils.h"
#include "pac_varfield.h"
ArrayType::ArrayType(Type* elemtype, Expr* length) : Type(ARRAY), elemtype_(elemtype), length_(length) {
init();
switch ( elemtype_->tot() ) {
case BUILTIN:
case PARAMETERIZED:
case STRING:
case EXTERN: break;
case ARRAY:
case CASE:
case DUMMY:
case EMPTY:
case RECORD:
case UNDEF: ASSERT(0); break;
}
}
void ArrayType::init() {
arraylength_var_field_ = nullptr;
elem_it_var_field_ = nullptr;
elem_var_field_ = nullptr;
elem_dataptr_var_field_ = nullptr;
elem_input_var_field_ = nullptr;
elem_dataptr_until_expr_ = nullptr;
end_of_array_loop_label_ = "@@@";
vector_str_ = strfmt("vector<%s>", elemtype_->DataTypeStr().c_str());
datatype_str_ = strfmt("%s*", vector_str_.c_str());
attr_generic_until_expr_ = nullptr;
attr_until_element_expr_ = nullptr;
attr_until_input_expr_ = nullptr;
}
ArrayType::~ArrayType() {
delete arraylength_var_field_;
delete elem_it_var_field_;
delete elem_var_field_;
delete elem_dataptr_var_field_;
delete elem_input_var_field_;
delete elem_dataptr_until_expr_;
}
Type* ArrayType::DoClone() const {
Type* elemtype = elemtype_->Clone();
if ( ! elemtype )
return nullptr;
return new ArrayType(elemtype, length_);
}
bool ArrayType::DefineValueVar() const { return true; }
string ArrayType::DataTypeStr() const { return datatype_str_; }
Type* ArrayType::ElementDataType() const { return elemtype_; }
string ArrayType::EvalElement(const string& array, const string& index) const {
if ( attr_transient_ )
throw Exception(this, "cannot access element in &transient array");
return strfmt("(*(%s))[%s]", array.c_str(), index.c_str());
}
const ID* ArrayType::arraylength_var() const { return arraylength_var_field_ ? arraylength_var_field_->id() : nullptr; }
const ID* ArrayType::elem_it_var() const { return elem_it_var_field_ ? elem_it_var_field_->id() : nullptr; }
const ID* ArrayType::elem_var() const { return elem_var_field_ ? elem_var_field_->id() : nullptr; }
const ID* ArrayType::elem_dataptr_var() const {
return elem_dataptr_var_field_ ? elem_dataptr_var_field_->id() : nullptr;
}
const ID* ArrayType::elem_input_var() const { return elem_input_var_field_ ? elem_input_var_field_->id() : nullptr; }
void ArrayType::ProcessAttr(Attr* a) {
Type::ProcessAttr(a);
switch ( a->type() ) {
case ATTR_RESTOFDATA: {
if ( elemtype_->StaticSize(env()) != 1 ) {
throw Exception(elemtype_,
"&restofdata can be applied"
" to only byte arrays");
}
if ( length_ ) {
throw Exception(length_,
"&restofdata cannot be applied"
" to arrays with specified length");
}
attr_restofdata_ = true;
// As the array automatically extends to the end of
// data, we do not have to check boundary.
SetBoundaryChecked();
} break;
case ATTR_RESTOFFLOW:
attr_restofflow_ = true;
// TODO: handle &restofflow
break;
case ATTR_UNTIL: {
bool ref_element = a->expr()->HasReference(element_macro_id);
bool ref_input = a->expr()->HasReference(input_macro_id);
if ( ref_element && ref_input ) {
throw Exception(a->expr(),
"cannot reference both $element and $input "
"in the same &until---please separate them.");
}
if ( ref_element ) {
if ( attr_until_element_expr_ ) {
throw Exception(a->expr(), "multiple &until on $element");
}
attr_until_element_expr_ = a->expr();
}
else if ( ref_input ) {
if ( attr_until_input_expr_ ) {
throw Exception(a->expr(), "multiple &until on $input");
}
attr_until_input_expr_ = a->expr();
}
else {
if ( attr_generic_until_expr_ ) {
throw Exception(a->expr(), "multiple &until condition");
}
attr_generic_until_expr_ = a->expr();
}
} break;
default: break;
}
}
void ArrayType::Prepare(Env* env, int flags) {
if ( flags & TO_BE_PARSED ) {
ID* arraylength_var = new ID(strfmt("%s__arraylength", value_var()->Name()));
ID* elem_var = new ID(strfmt("%s__elem", value_var()->Name()));
ID* elem_it_var = new ID(strfmt("%s__it", elem_var->Name()));
elem_var_field_ = new ParseVarField(Field::CLASS_MEMBER, elem_var, elemtype_);
AddField(elem_var_field_);
if ( incremental_parsing() ) {
arraylength_var_field_ = new PrivVarField(arraylength_var, extern_type_int->Clone());
elem_it_var_field_ = new PrivVarField(elem_it_var, extern_type_int->Clone());
AddField(arraylength_var_field_);
AddField(elem_it_var_field_);
}
else {
arraylength_var_field_ = new TempVarField(arraylength_var, extern_type_int->Clone());
elem_it_var_field_ = new TempVarField(elem_it_var, extern_type_int->Clone());
arraylength_var_field_->Prepare(env);
elem_it_var_field_->Prepare(env);
// Add elem_dataptr_var only when not parsing incrementally
ID* elem_dataptr_var = new ID(strfmt("%s__dataptr", elem_var->Name()));
elem_dataptr_var_field_ = new TempVarField(elem_dataptr_var, extern_type_const_byteptr->Clone());
elem_dataptr_var_field_->Prepare(env);
// until(dataptr >= end_of_data)
elem_dataptr_until_expr_ =
new Expr(Expr::EXPR_GE, new Expr(elem_dataptr_var->clone()), new Expr(end_of_data->clone()));
}
if ( attr_until_input_expr_ ) {
elemtype_->SetUntilCheck(this);
}
end_of_array_loop_label_ = strfmt("end_of_%s", value_var()->Name());
}
Type::Prepare(env, flags);
}
void ArrayType::GenArrayLength(Output* out_cc, Env* env, const DataPtr& data) {
if ( env->Evaluated(arraylength_var()) )
return;
if ( ! incremental_parsing() ) {
arraylength_var_field_->GenTempDecls(out_cc, env);
// This is about to get initialized below, don't initialize it twice.
if ( ! length_ && ! attr_restofdata_ )
arraylength_var_field_->GenInitCode(out_cc, env);
}
if ( length_ ) {
out_cc->println("%s = %s;", env->LValue(arraylength_var()), length_->EvalExpr(out_cc, env));
env->SetEvaluated(arraylength_var());
// Check negative array length
out_cc->println("if ( %s < 0 ) {", env->LValue(arraylength_var()));
out_cc->inc_indent();
out_cc->println("throw binpac::ExceptionOutOfBound(\"%s\",", data_id_str_.c_str());
out_cc->println(" %s, (%s) - (%s));", env->LValue(arraylength_var()), env->RValue(end_of_data),
env->RValue(begin_of_data));
out_cc->dec_indent();
out_cc->println("}");
int element_size;
if ( elemtype_->StaticSize(env) == -1 ) {
// Check for overlong array quantity. We cap it at the maximum
// array size (assume 1-byte elements * array length) as we can't
// possibly store more elements. e.g. this helps prevent
// user-controlled length fields from causing an excessive
// iteration and/or memory-allocation (for the array we'll be
// parsing into) unless they actually sent enough data to go along
// with it. Note that this check is *not* looking for whether the
// contents of the array will extend past the end of the data
// buffer.
out_cc->println("// Check array element quantity: %s", data_id_str_.c_str());
element_size = 1;
}
else {
// Boundary check the entire array if elements have static size.
out_cc->println("// Check bounds for static-size array: %s", data_id_str_.c_str());
elemtype_->SetBoundaryChecked();
element_size = elemtype_->StaticSize(env);
if ( element_size == 0 ) {
// If we know we have an array of empty elements, probably
// better to structure the parser as just a single empty
// field to avoid DoS vulnerability of allocating
// arbitrary number of empty records (i.e. cheap for them,
// but costly for us unless we have special optimization
// for this scenario to forgo the usual allocation).
throw Exception(this, "using an array of known-to-be-empty elements is possibly a bad idea");
}
}
const char* array_ptr_expr = data.ptr_expr();
string max_elements_available =
strfmt("((%s - %s) / %d)", env->RValue(end_of_data), array_ptr_expr, element_size);
out_cc->println("if ( %s > %s )", env->RValue(arraylength_var()), max_elements_available.c_str());
out_cc->inc_indent();
out_cc->println("throw binpac::ExceptionOutOfBound(\"%s\",", data_id_str_.c_str());
out_cc->println(" %s, (%s) - (%s));", env->RValue(arraylength_var()), env->RValue(end_of_data),
array_ptr_expr);
out_cc->dec_indent();
}
else if ( attr_restofdata_ ) {
ASSERT(elemtype_->StaticSize(env) == 1);
out_cc->println("%s = (%s) - (%s);", env->LValue(arraylength_var()), env->RValue(end_of_data), data.ptr_expr());
env->SetEvaluated(arraylength_var());
}
}
void ArrayType::GenPubDecls(Output* out_h, Env* env) {
Type::GenPubDecls(out_h, env);
if ( declared_as_type() ) {
if ( attr_transient_ )
throw Exception(this, "cannot access element in &transient array");
out_h->println("int size() const { return %s ? %s->size() : 0; }", env->RValue(value_var()),
env->RValue(value_var()));
out_h->println("%s operator[](int index) const { BINPAC_ASSERT(%s); return (*%s)[index]; }",
elemtype_->DataTypeConstRefStr().c_str(), env->RValue(value_var()), env->RValue(value_var()));
}
}
void ArrayType::GenPrivDecls(Output* out_h, Env* env) {
ASSERT(elem_var_field_->type() == elemtype_);
ASSERT(elemtype_->value_var());
Type::GenPrivDecls(out_h, env);
}
void ArrayType::GenInitCode(Output* out_cc, Env* env) {
// Do not initiate the array here
// out_cc->println("%s = new %s;", lvalue(), vector_str_.c_str());
out_cc->println("%s = nullptr;", lvalue());
Type::GenInitCode(out_cc, env);
if ( incremental_parsing() ) {
out_cc->println("%s = -1;", env->LValue(elem_it_var()));
}
}
void ArrayType::GenCleanUpCode(Output* out_cc, Env* env) {
Type::GenCleanUpCode(out_cc, env);
if ( elemtype_->NeedsCleanUp() ) {
if ( ! elem_var_field_ ) {
ID* elem_var = new ID(strfmt("%s__elem", value_var()->Name()));
elem_var_field_ = new ParseVarField(Field::NOT_CLASS_MEMBER, elem_var, elemtype_);
elem_var_field_->Prepare(env);
}
out_cc->println("if ( %s ) {", env->RValue(value_var()));
out_cc->inc_indent();
out_cc->println("for ( auto* %s : *%s ) {", env->LValue(elem_var()), env->RValue(value_var()));
out_cc->inc_indent();
elemtype_->GenCleanUpCode(out_cc, env);
out_cc->dec_indent();
out_cc->println("}");
out_cc->dec_indent();
out_cc->println("}");
}
out_cc->println("delete %s;", lvalue());
}
string ArrayType::GenArrayInit(Output* out_cc, Env* env, bool known_array_length) {
string array_str;
array_str = lvalue();
if ( incremental_parsing() ) {
out_cc->println("if ( %s < 0 ) {", env->LValue(elem_it_var()));
out_cc->inc_indent();
out_cc->println("// Initialize only once");
out_cc->println("%s = 0;", env->LValue(elem_it_var()));
}
out_cc->println("%s = new %s;", lvalue(), vector_str_.c_str());
if ( known_array_length ) {
out_cc->println("%s->reserve(%s);", lvalue(), env->RValue(arraylength_var()));
}
if ( incremental_parsing() ) {
out_cc->dec_indent();
out_cc->println("}");
}
return array_str;
}
void ArrayType::GenElementAssignment(Output* out_cc, Env* env, string const& array_str, bool use_vector) {
if ( attr_transient_ ) {
// Just discard.
out_cc->println("delete %s;", env->LValue(elem_var()));
return;
}
// Assign the element
if ( ! use_vector ) {
out_cc->println("%s[%s] = %s;", array_str.c_str(), env->LValue(elem_it_var()), env->LValue(elem_var()));
}
else {
out_cc->println("%s->push_back(%s);", array_str.c_str(), env->LValue(elem_var()));
}
}
void ArrayType::DoGenParseCode(Output* out_cc, Env* env, const DataPtr& data, int flags) {
GenArrayLength(out_cc, env, data);
// Otherwise these variables are declared as member variables
if ( ! incremental_parsing() ) {
// Declare and initialize temporary variables
elem_var_field_->GenInitCode(out_cc, env);
elem_it_var_field_->GenTempDecls(out_cc, env);
out_cc->println("%s = 0;", env->LValue(elem_it_var()));
env->SetEvaluated(elem_it_var());
}
/*
If the input length can be determined without parsing
individual elements, generate the boundary checking before
parsing (unless in the case of incremental parsing).
There are two cases when the input length can be determined:
1. The array has a static size;
2. The array length can be computed before parsing and
each element is of constant size.
*/
bool compute_size_var = false;
if ( incremental_input() ) {
// Do not compute size_var on incremental input
compute_size_var = false;
if ( ! incremental_parsing() &&
(StaticSize(env) >= 0 || (env->Evaluated(arraylength_var()) && elemtype_->StaticSize(env) >= 0)) ) {
GenBoundaryCheck(out_cc, env, data);
}
}
else {
compute_size_var = AddSizeVar(out_cc, env);
}
bool known_array_length = env->Evaluated(arraylength_var());
string array_str = GenArrayInit(out_cc, env, known_array_length);
bool use_vector = true;
ASSERT(elem_it_var());
DataPtr elem_data(env, nullptr, 0);
if ( elem_dataptr_var() ) {
out_cc->println("const_byteptr %s = %s;", env->LValue(elem_dataptr_var()), data.ptr_expr());
env->SetEvaluated(elem_dataptr_var());
elem_data = DataPtr(env, elem_dataptr_var(), 0);
}
string for_condition = known_array_length ?
strfmt("%s < %s", env->LValue(elem_it_var()), env->RValue(arraylength_var())) :
"/* forever */";
out_cc->println("for (; %s; ++%s) {", for_condition.c_str(), env->LValue(elem_it_var()));
out_cc->inc_indent();
if ( attr_generic_until_expr_ )
GenUntilCheck(out_cc, env, attr_generic_until_expr_, true);
if ( elem_dataptr_var() ) {
if ( length_ ) {
// Array has a known-length expression like uint16[4] vs. uint16[].
// Here, arriving at the end of the data buffer should not be a
// valid loop-termination condition (which is what the
// GenUntilCheck() call produces). Instead, rely on the loop
// counter to terminate iteration or else the parsing code
// generated for each element should throw an OOB exception if
// there's insufficient data in the buffer.
}
else {
GenUntilCheck(out_cc, env, elem_dataptr_until_expr_, false);
}
}
elemtype_->GenPreParsing(out_cc, env);
elemtype_->GenParseCode(out_cc, env, elem_data, flags);
if ( incremental_parsing() ) {
out_cc->println("if ( ! %s )", elemtype_->parsing_complete(env).c_str());
out_cc->inc_indent();
out_cc->println("goto %s;", kNeedMoreData);
out_cc->dec_indent();
}
GenElementAssignment(out_cc, env, array_str, use_vector);
if ( elem_dataptr_var() ) {
out_cc->println("%s += %s;", env->LValue(elem_dataptr_var()),
elemtype_->DataSize(nullptr, env, elem_data).c_str());
out_cc->println("BINPAC_ASSERT(%s <= %s);", env->RValue(elem_dataptr_var()), env->RValue(end_of_data));
}
if ( attr_until_element_expr_ )
GenUntilCheck(out_cc, env, attr_until_element_expr_, false);
if ( elemtype_->IsPointerType() )
out_cc->println("%s = nullptr;", env->LValue(elem_var()));
out_cc->dec_indent();
out_cc->println("}");
out_cc->dec_indent();
out_cc->println("%s: ;", end_of_array_loop_label_.c_str());
out_cc->inc_indent();
if ( compute_size_var && elem_dataptr_var() && ! env->Evaluated(size_var()) ) {
// Compute the data size
out_cc->println("%s = %s - (%s);", env->LValue(size_var()), env->RValue(elem_dataptr_var()), data.ptr_expr());
env->SetEvaluated(size_var());
}
}
void ArrayType::GenUntilInputCheck(Output* out_cc, Env* env) {
ID* elem_input_var_id = new ID(strfmt("%s__elem_input", value_var()->Name()));
elem_input_var_field_ = new TempVarField(elem_input_var_id, extern_type_const_bytestring->Clone());
elem_input_var_field_->Prepare(env);
out_cc->println("%s %s(%s, %s);", extern_type_const_bytestring->DataTypeStr().c_str(),
env->LValue(elem_input_var()), env->RValue(begin_of_data), env->RValue(end_of_data));
env->SetEvaluated(elem_input_var());
GenUntilCheck(out_cc, env, attr_until_input_expr_, true);
}
void ArrayType::GenUntilCheck(Output* out_cc, Env* env, Expr* until_expr, bool delete_elem) {
ASSERT(until_expr);
Env check_env(env, this);
check_env.AddMacro(element_macro_id, new Expr(elem_var()->clone()));
if ( elem_input_var() ) {
check_env.AddMacro(input_macro_id, new Expr(elem_input_var()->clone()));
}
out_cc->println("// Check &until(%s)", until_expr->orig());
out_cc->println("if ( %s ) {", until_expr->EvalExpr(out_cc, &check_env));
out_cc->inc_indent();
if ( parsing_complete_var() ) {
out_cc->println("%s = true;", env->LValue(parsing_complete_var()));
}
if ( elemtype_->IsPointerType() ) {
if ( delete_elem )
elemtype_->GenCleanUpCode(out_cc, env);
else
out_cc->println("%s = nullptr;", env->LValue(elem_var()));
}
out_cc->println("goto %s;", end_of_array_loop_label_.c_str());
out_cc->dec_indent();
out_cc->println("}");
}
void ArrayType::GenDynamicSize(Output* out_cc, Env* env, const DataPtr& data) {
ASSERT(! incremental_input());
DEBUG_MSG("Generating dynamic size for array `%s'\n", value_var()->Name());
int elem_w = elemtype_->StaticSize(env);
if ( elem_w >= 0 && ! attr_until_element_expr_ && ! attr_until_input_expr_ && (length_ || attr_restofdata_) ) {
// If the elements have a fixed size,
// we only need to compute the number of elements
bool compute_size_var = AddSizeVar(out_cc, env);
ASSERT(compute_size_var);
GenArrayLength(out_cc, env, data);
ASSERT(env->Evaluated(arraylength_var()));
out_cc->println("%s = %d * %s;", env->LValue(size_var()), elem_w, env->RValue(arraylength_var()));
env->SetEvaluated(size_var());
}
else {
// Otherwise we need parse the array dynamically
GenParseCode(out_cc, env, data, 0);
}
}
int ArrayType::StaticSize(Env* env) const {
int num = 0;
if ( ! length_ || ! length_->ConstFold(env, &num) )
return -1;
int elem_w = elemtype_->StaticSize(env);
if ( elem_w < 0 )
return -1;
DEBUG_MSG("static size of %s:%s = %d * %d\n", decl_id()->Name(), lvalue(), elem_w, num);
return num * elem_w;
}
void ArrayType::SetBoundaryChecked() {
Type::SetBoundaryChecked();
if ( attr_length_expr_ ) {
// When using &length on an array, only treat its elements as
// already-bounds-checked if they are a single byte in length.
if ( elemtype_->StaticSize(env()) == 1 )
elemtype_->SetBoundaryChecked();
return;
}
elemtype_->SetBoundaryChecked();
}
void ArrayType::DoMarkIncrementalInput() { elemtype_->MarkIncrementalInput(); }
bool ArrayType::RequiresAnalyzerContext() {
return Type::RequiresAnalyzerContext() || (length_ && length_->RequiresAnalyzerContext()) ||
elemtype_->RequiresAnalyzerContext();
}
bool ArrayType::DoTraverse(DataDepVisitor* visitor) {
if ( ! Type::DoTraverse(visitor) )
return false;
if ( length_ && ! length_->Traverse(visitor) )
return false;
if ( ! elemtype_->Traverse(visitor) )
return false;
return true;
}

View file

@ -1,88 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_array_h
#define pac_array_h
#include "pac_common.h"
#include "pac_type.h"
// Fixed-length array and variable length sequence with an ending pattern
class ArrayType : public Type {
public:
ArrayType(Type* arg_elemtype, Expr* arg_length = nullptr);
~ArrayType() override;
bool DefineValueVar() const override;
string DataTypeStr() const override;
string DefaultValue() const override { return "0"; }
Type* ElementDataType() const override;
string EvalElement(const string& array, const string& index) const override;
void ProcessAttr(Attr* a) override;
void Prepare(Env* env, int flags) override;
void GenPubDecls(Output* out, Env* env) override;
void GenPrivDecls(Output* out, Env* env) override;
void GenInitCode(Output* out, Env* env) override;
void GenCleanUpCode(Output* out, Env* env) override;
int StaticSize(Env* env) const override;
void SetBoundaryChecked() override;
void GenUntilInputCheck(Output* out_cc, Env* env);
bool IsPointerType() const override { return true; }
protected:
void init();
void DoGenParseCode(Output* out, Env* env, const DataPtr& data, int flags) override;
void GenDynamicSize(Output* out, Env* env, const DataPtr& data) override;
void GenArrayLength(Output* out_cc, Env* env, const DataPtr& data);
string GenArrayInit(Output* out_cc, Env* env, bool known_array_length);
void GenElementAssignment(Output* out_cc, Env* env, string const& array_str, bool use_vector);
void GenUntilCheck(Output* out_cc, Env* env, Expr* until_condition, bool delete_elem);
bool ByteOrderSensitive() const override { return elemtype_->RequiresByteOrder(); }
bool RequiresAnalyzerContext() override;
Type* DoClone() const override;
void DoMarkIncrementalInput() override;
const ID* arraylength_var() const;
const ID* elem_it_var() const;
const ID* elem_var() const;
const ID* elem_dataptr_var() const;
const ID* elem_input_var() const;
protected:
bool DoTraverse(DataDepVisitor* visitor) override;
private:
Type* elemtype_;
Expr* length_;
string vector_str_;
string datatype_str_;
string end_of_array_loop_label_;
Field* arraylength_var_field_;
Field* elem_it_var_field_;
Field* elem_var_field_;
Field* elem_dataptr_var_field_;
Field* elem_input_var_field_;
// This does not come from &until, but is internally generated
Expr* elem_dataptr_until_expr_;
Expr* attr_generic_until_expr_;
Expr* attr_until_element_expr_;
Expr* attr_until_input_expr_;
};
#endif // pac_array_h

View file

@ -1,50 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_attr.h"
#include "pac_expr.h"
bool Attr::DoTraverse(DataDepVisitor* visitor) {
if ( expr_ && ! expr_->Traverse(visitor) )
return false;
return true;
}
bool Attr::RequiresAnalyzerContext() const { return (expr_ && expr_->RequiresAnalyzerContext()); }
void Attr::init() {
expr_ = nullptr;
seqend_ = nullptr;
delete_expr_ = false;
}
Attr::Attr(AttrType type) : DataDepElement(DataDepElement::ATTR) {
type_ = type;
init();
}
Attr::Attr(AttrType type, Expr* expr) : DataDepElement(DataDepElement::ATTR) {
type_ = type;
init();
expr_ = expr;
}
Attr::Attr(AttrType type, ExprList* exprlist) : DataDepElement(DataDepElement::ATTR) {
type_ = type;
init();
expr_ = new Expr(exprlist);
delete_expr_ = true;
}
Attr::Attr(AttrType type, SeqEnd* seqend) : DataDepElement(DataDepElement::ATTR) {
type_ = type;
init();
seqend_ = seqend;
}
Attr::~Attr() {
if ( delete_expr_ )
delete expr_;
}
LetAttr::LetAttr(FieldList* letfields) : Attr(ATTR_LET) { letfields_ = letfields; }

View file

@ -1,65 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_attr_h
#define pac_attr_h
#include "pac_common.h"
#include "pac_datadep.h"
enum AttrType {
ATTR_BYTEORDER,
ATTR_CHECK,
ATTR_CHUNKED,
ATTR_ENFORCE,
ATTR_EXPORTSOURCEDATA,
ATTR_IF,
ATTR_LENGTH,
ATTR_LET,
ATTR_LINEBREAKER,
ATTR_MULTILINE,
ATTR_ONELINE,
ATTR_REFCOUNT,
ATTR_REQUIRES,
ATTR_RESTOFDATA,
ATTR_RESTOFFLOW,
ATTR_TRANSIENT,
ATTR_UNTIL,
};
class Attr : public Object, public DataDepElement {
public:
Attr(AttrType type);
Attr(AttrType type, Expr* expr);
Attr(AttrType type, ExprList* exprlist);
Attr(AttrType type, SeqEnd* seqend);
~Attr() override;
AttrType type() const { return type_; }
Expr* expr() const { return expr_; }
SeqEnd* seqend() const { return seqend_; }
bool RequiresAnalyzerContext() const;
protected:
bool DoTraverse(DataDepVisitor* visitor) override;
protected:
void init();
AttrType type_;
Expr* expr_;
SeqEnd* seqend_;
bool delete_expr_;
};
class LetAttr : public Attr {
public:
LetAttr(FieldList* letfields);
FieldList* letfields() const { return letfields_; }
private:
FieldList* letfields_;
};
#endif // pac_attr_h

View file

@ -1,119 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_btype.h"
#include "pac_dataptr.h"
#include "pac_id.h"
#include "pac_output.h"
Type* BuiltInType::DoClone() const { return new BuiltInType(bit_type()); }
bool BuiltInType::IsNumericType() const {
BITType t = bit_type();
return (t == INT8 || t == INT16 || t == INT32 || t == INT64 || t == UINT8 || t == UINT16 || t == UINT32 ||
t == UINT64);
}
bool BuiltInType::CompatibleBuiltInTypes(BuiltInType* type1, BuiltInType* type2) {
return type1->IsNumericType() && type2->IsNumericType();
}
static const char* basic_pactype_name[] = {
#define TYPE_DEF(name, pactype, ctype, size) pactype,
#include "pac_type.def"
#undef TYPE_DEF
nullptr,
};
void BuiltInType::static_init() {
for ( int bit_type = 0; basic_pactype_name[bit_type]; ++bit_type ) {
Type::AddPredefinedType(basic_pactype_name[bit_type], new BuiltInType((BITType)bit_type));
}
}
int BuiltInType::LookUpByName(const char* name) {
ASSERT(0);
for ( int i = 0; basic_pactype_name[i]; ++i )
if ( strcmp(basic_pactype_name[i], name) == 0 )
return i;
return -1;
}
static const char* basic_ctype_name[] = {
#define TYPE_DEF(name, pactype, ctype, size) ctype,
#include "pac_type.def"
#undef TYPE_DEF
nullptr,
};
bool BuiltInType::DefineValueVar() const { return bit_type_ != EMPTY; }
string BuiltInType::DataTypeStr() const { return basic_ctype_name[bit_type_]; }
int BuiltInType::StaticSize(Env* /* env */) const {
static const size_t basic_type_size[] = {
#define TYPE_DEF(name, pactype, ctype, size) size,
#include "pac_type.def"
#undef TYPE_DEF
};
return basic_type_size[bit_type_];
}
void BuiltInType::DoMarkIncrementalInput() {
if ( bit_type_ == EMPTY )
return;
Type::DoMarkIncrementalInput();
}
void BuiltInType::GenInitCode(Output* out_cc, Env* env) {
if ( bit_type_ != EMPTY )
out_cc->println("%s = 0;", env->LValue(value_var()));
Type::GenInitCode(out_cc, env);
}
void BuiltInType::GenDynamicSize(Output* out_cc, Env* env, const DataPtr& data) {
/* should never be called */
ASSERT(0);
}
void BuiltInType::DoGenParseCode(Output* out_cc, Env* env, const DataPtr& data, int flags) {
if ( bit_type_ == EMPTY )
return;
// There is no need to generate the size variable
// out_cc->println("%s = sizeof(%s);", size_var(), DataTypeStr().c_str());
GenBoundaryCheck(out_cc, env, data);
if ( anonymous_value_var() )
return;
switch ( bit_type_ ) {
case EMPTY:
// do nothing
break;
case INT8:
case UINT8:
out_cc->println("%s = *((%s const*)(%s));", lvalue(), DataTypeStr().c_str(), data.ptr_expr());
break;
case INT16:
case UINT16:
case INT32:
case UINT32:
case INT64:
case UINT64:
#if 0
out_cc->println("%s = UnMarshall<%s>(%s, %s);",
lvalue(),
DataTypeStr().c_str(),
data.ptr_expr(),
EvalByteOrder(out_cc, env).c_str());
#else
out_cc->println("%s = FixByteOrder(%s, *((%s const*)(%s)));", lvalue(), EvalByteOrder(out_cc, env).c_str(),
DataTypeStr().c_str(), data.ptr_expr());
#endif
break;
}
}

View file

@ -1,50 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_btype_h
#define pac_btype_h
#include "pac_type.h"
class BuiltInType : public Type {
public:
enum BITType {
#define TYPE_DEF(name, pactype, ctype, size) name,
#include "pac_type.def"
#undef TYPE_DEF
};
static int LookUpByName(const char* name);
BuiltInType(BITType bit_type) : Type(bit_type == BuiltInType::EMPTY ? Type::EMPTY : BUILTIN), bit_type_(bit_type) {}
BITType bit_type() const { return bit_type_; }
bool IsNumericType() const override;
bool DefineValueVar() const override;
string DataTypeStr() const override;
string DefaultValue() const override { return "0"; }
int StaticSize(Env* env) const override;
bool IsPointerType() const override { return false; }
bool ByteOrderSensitive() const override { return StaticSize(0) >= 2; }
void GenInitCode(Output* out_cc, Env* env) override;
void DoMarkIncrementalInput() override;
protected:
void DoGenParseCode(Output* out, Env* env, const DataPtr& data, int flags) override;
void GenDynamicSize(Output* out, Env* env, const DataPtr& data) override;
Type* DoClone() const override;
BITType bit_type_;
public:
static void static_init();
static bool CompatibleBuiltInTypes(BuiltInType* type1, BuiltInType* type2);
};
#endif // pac_btype_h

View file

@ -1,406 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_case.h"
#include <stdint.h>
#include <limits>
#include "pac_btype.h"
#include "pac_exception.h"
#include "pac_expr.h"
#include "pac_exttype.h"
#include "pac_id.h"
#include "pac_output.h"
#include "pac_typedecl.h"
#include "pac_utils.h"
CaseType::CaseType(Expr* index_expr, CaseFieldList* cases) : Type(CASE), index_expr_(index_expr), cases_(cases) {
index_var_ = nullptr;
foreach (i, CaseFieldList, cases_)
AddField(*i);
}
CaseType::~CaseType() {
delete index_var_;
delete index_expr_;
delete cases_;
}
void CaseType::AddCaseField(CaseField* f) {
// All fields must be added before Prepare()
ASSERT(! env());
AddField(f);
cases_->push_back(f);
}
bool CaseType::DefineValueVar() const { return false; }
string CaseType::DataTypeStr() const {
ASSERT(type_decl());
return strfmt("%s*", type_decl()->class_name().c_str());
}
Type* CaseType::ValueType() const {
foreach (i, CaseFieldList, cases_) {
CaseField* c = *i;
return c->type();
}
ASSERT(0);
return nullptr;
}
string CaseType::DefaultValue() const { return ValueType()->DefaultValue(); }
void CaseType::Prepare(Env* env, int flags) {
ASSERT(flags & TO_BE_PARSED);
index_var_ = new ID(strfmt("%s_case_index", value_var()->Name()));
// Unable to get the type for index_var_ at this moment, but we'll
// generate the right type based on index_expr_ later.
env->AddID(index_var_, MEMBER_VAR, nullptr);
// Sort the cases_ to put the default case at the end of the list
CaseFieldList::iterator default_case_it = cases_->end(); // to avoid warning
CaseField* default_case = nullptr;
foreach (i, CaseFieldList, cases_) {
CaseField* c = *i;
if ( ! c->index() ) {
if ( default_case )
throw Exception(c, "duplicate default case");
default_case_it = i;
default_case = c;
}
}
if ( default_case ) {
cases_->erase(default_case_it);
cases_->push_back(default_case);
}
foreach (i, CaseFieldList, cases_) {
CaseField* c = *i;
c->set_index_var(index_var_);
c->set_case_type(this);
}
Type::Prepare(env, flags);
}
void CaseType::GenPrivDecls(Output* out_h, Env* env) {
Type* t = index_expr_->DataType(env);
if ( t->tot() != Type::BUILTIN )
// It's a Type::EXTERN with a C++ type of "int", "bool", or "enum",
// any of which will convert consistently using an int as storage type.
t = extern_type_int;
out_h->println("%s %s;", t->DataTypeStr().c_str(), env->LValue(index_var_));
Type::GenPrivDecls(out_h, env);
}
void CaseType::GenPubDecls(Output* out_h, Env* env) {
Type* t = index_expr_->DataType(env);
if ( t->tot() != Type::BUILTIN )
t = extern_type_int;
out_h->println("%s %s const { return %s; }", t->DataTypeStr().c_str(), env->RValue(index_var_),
env->LValue(index_var_));
Type::GenPubDecls(out_h, env);
}
void CaseType::GenInitCode(Output* out_cc, Env* env) {
out_cc->println("%s = -1;", env->LValue(index_var_));
Type::GenInitCode(out_cc, env);
}
void CaseType::GenCleanUpCode(Output* out_cc, Env* env) {
Type::GenCleanUpCode(out_cc, env);
env->set_in_branch(true);
out_cc->println("// NOLINTBEGIN(bugprone-branch-clone)");
out_cc->println("switch ( %s ) {", env->RValue(index_var_));
out_cc->inc_indent();
foreach (i, CaseFieldList, cases_) {
CaseField* c = *i;
c->GenCleanUpCode(out_cc, env);
}
out_cc->dec_indent();
out_cc->println("}");
out_cc->println("// NOLINTEND(bugprone-branch-clone)");
env->set_in_branch(false);
}
void CaseType::DoGenParseCode(Output* out_cc, Env* env, const DataPtr& data, int flags) {
if ( StaticSize(env) >= 0 )
GenBoundaryCheck(out_cc, env, data);
bool compute_size_var = false;
if ( ! incremental_input() )
compute_size_var = AddSizeVar(out_cc, env);
out_cc->println("%s = %s;", env->LValue(index_var_), index_expr_->EvalExpr(out_cc, env));
env->SetEvaluated(index_var_);
env->set_in_branch(true);
out_cc->println("// NOLINTBEGIN(bugprone-branch-clone)");
out_cc->println("switch ( %s ) {", env->RValue(index_var_));
out_cc->inc_indent();
bool has_default_case = false;
foreach (i, CaseFieldList, cases_) {
CaseField* c = *i;
c->GenParseCode(out_cc, env, data, compute_size_var ? size_var() : nullptr);
if ( c->IsDefaultCase() )
has_default_case = true;
}
if ( ! has_default_case ) {
out_cc->println("default:");
out_cc->inc_indent();
out_cc->println("throw binpac::ExceptionInvalidCaseIndex(\"%s\", (int64)%s);", decl_id()->Name(),
env->RValue(index_var_));
out_cc->println("break;");
out_cc->dec_indent();
}
out_cc->dec_indent();
out_cc->println("}");
out_cc->println("// NOLINTEND(bugprone-branch-clone)");
env->set_in_branch(false);
if ( compute_size_var )
env->SetEvaluated(size_var());
}
void CaseType::GenDynamicSize(Output* out_cc, Env* env, const DataPtr& data) { GenParseCode(out_cc, env, data, 0); }
int CaseType::StaticSize(Env* env) const {
int static_w = -1;
foreach (i, CaseFieldList, cases_) {
CaseField* c = *i;
int w = c->StaticSize(env);
if ( w < 0 || (static_w >= 0 && w != static_w) )
return -1;
static_w = w;
}
return static_w;
}
void CaseType::SetBoundaryChecked() {
Type::SetBoundaryChecked();
foreach (i, CaseFieldList, cases_) {
CaseField* c = *i;
c->SetBoundaryChecked();
}
}
void CaseType::DoMarkIncrementalInput() {
foreach (i, CaseFieldList, cases_) {
CaseField* c = *i;
c->type()->MarkIncrementalInput();
}
}
bool CaseType::ByteOrderSensitive() const {
foreach (i, CaseFieldList, cases_) {
CaseField* c = *i;
if ( c->RequiresByteOrder() )
return true;
}
return false;
}
CaseField::CaseField(ExprList* index, ID* id, Type* type)
: Field(CASE_FIELD, TYPE_TO_BE_PARSED | CLASS_MEMBER | PUBLIC_READABLE, id, type), index_(index) {
ASSERT(type_);
type_->set_value_var(id, MEMBER_VAR);
case_type_ = nullptr;
index_var_ = nullptr;
}
CaseField::~CaseField() { delete_list(ExprList, index_); }
void GenCaseStr(ExprList* index_list, Output* out_cc, Env* env, Type* switch_type) {
if ( index_list ) {
foreach (i, ExprList, index_list) {
Expr* index_expr = *i;
Type* case_type = index_expr->DataType(env);
if ( case_type->tot() == Type::BUILTIN && case_type->StaticSize(env) > 4 )
throw ExceptionInvalidCaseSizeExpr(index_expr);
int index_const;
if ( ! index_expr->ConstFold(env, &index_const) )
throw ExceptionNonConstExpr(index_expr);
// External C++ types like "int", "bool", "enum"
// all use "int" type internally by default.
int case_type_width = 4;
int switch_type_width = 4;
if ( switch_type->tot() == Type::BUILTIN )
switch_type_width = switch_type->StaticSize(env);
if ( case_type->tot() == Type::BUILTIN )
case_type_width = case_type->StaticSize(env);
if ( case_type_width > switch_type_width ) {
BuiltInType* st = (BuiltInType*)switch_type;
if ( switch_type_width == 1 ) {
if ( st->bit_type() == BuiltInType::INT8 ) {
if ( index_const < std::numeric_limits<int8_t>::min() )
throw ExceptionInvalidCaseLimitExpr(index_expr);
if ( index_const > std::numeric_limits<int8_t>::max() )
throw ExceptionInvalidCaseLimitExpr(index_expr);
}
else {
if ( index_const < std::numeric_limits<uint8_t>::min() )
throw ExceptionInvalidCaseLimitExpr(index_expr);
if ( index_const > std::numeric_limits<uint8_t>::max() )
throw ExceptionInvalidCaseLimitExpr(index_expr);
}
}
else if ( switch_type_width == 2 ) {
if ( st->bit_type() == BuiltInType::INT16 ) {
if ( index_const < std::numeric_limits<int16_t>::min() )
throw ExceptionInvalidCaseLimitExpr(index_expr);
if ( index_const > std::numeric_limits<int16_t>::max() )
throw ExceptionInvalidCaseLimitExpr(index_expr);
}
else {
if ( index_const < std::numeric_limits<uint16_t>::min() )
throw ExceptionInvalidCaseLimitExpr(index_expr);
if ( index_const > std::numeric_limits<uint16_t>::max() )
throw ExceptionInvalidCaseLimitExpr(index_expr);
}
}
else {
assert(0);
}
}
// We're always using "int" for storage, so ok to just
// cast into the type used by the switch statement since
// some unsafe stuff is already checked above.
if ( ! switch_type->IsBooleanType() )
out_cc->println("case ((%s)%d):", switch_type->DataTypeStr().c_str(), index_const);
else
out_cc->println("case %s:", index_const == 0 ? "false" : "true");
}
}
else {
out_cc->println("default:");
}
}
void CaseField::Prepare(Env* env) {
ASSERT(index_var_);
Field::Prepare(env);
}
void CaseField::GenPubDecls(Output* out_h, Env* env) {
if ( ! ((flags_ & PUBLIC_READABLE) && (flags_ & CLASS_MEMBER)) )
return;
// Skip type "empty"
if ( type_->DataTypeStr().empty() )
return;
out_h->println("%s %s const {", type_->DataTypeConstRefStr().c_str(), env->RValue(id_));
out_h->inc_indent();
if ( ! index_ )
out_h->println("return %s;", lvalue());
else {
out_h->println("// NOLINTBEGIN(bugprone-branch-clone)");
out_h->println("switch ( %s ) {", env->RValue(index_var_));
out_h->inc_indent();
GenCaseStr(index_, out_h, env, case_type()->IndexExpr()->DataType(env));
out_h->inc_indent();
out_h->println("break; // OK");
out_h->dec_indent();
out_h->println("default:");
out_h->inc_indent();
out_h->println("throw binpac::ExceptionInvalidCase(\"%s\", (int64)%s, \"%s\");", id_->LocName(),
env->RValue(index_var_), OrigExprList(index_).c_str());
out_h->println("break;");
out_h->dec_indent();
out_h->dec_indent();
out_h->println("}");
out_h->println("// NOLINTEND(bugprone-branch-clone)");
out_h->println("return %s;", lvalue());
}
out_h->dec_indent();
out_h->println("}");
}
void CaseField::GenInitCode(Output* out_cc, Env* env) {
// GenCaseStr(index_, out_cc, env);
// out_cc->inc_indent();
// out_cc->println("{");
// out_cc->println("// Initialize \"%s\"", id_->Name());
type_->GenInitCode(out_cc, env);
// out_cc->println("}");
// out_cc->println("break;");
// out_cc->dec_indent();
}
void CaseField::GenCleanUpCode(Output* out_cc, Env* env) {
GenCaseStr(index_, out_cc, env, case_type()->IndexExpr()->DataType(env));
out_cc->inc_indent();
out_cc->println("// Clean up \"%s\"", id_->Name());
if ( ! anonymous_field() ) {
out_cc->println("{");
out_cc->inc_indent();
type_->GenCleanUpCode(out_cc, env);
out_cc->dec_indent();
out_cc->println("}");
}
else
out_cc->println("{}");
out_cc->println("break;");
out_cc->dec_indent();
}
void CaseField::GenParseCode(Output* out_cc, Env* env, const DataPtr& data, const ID* size_var) {
GenCaseStr(index_, out_cc, env, case_type()->IndexExpr()->DataType(env));
out_cc->inc_indent();
out_cc->println("// Parse \"%s\"", id_->Name());
out_cc->println("{");
out_cc->inc_indent();
{
Env case_env(env, this);
type_->GenPreParsing(out_cc, &case_env);
type_->GenParseCode(out_cc, &case_env, data, 0);
if ( size_var ) {
out_cc->println("%s = %s;", case_env.LValue(size_var), type_->DataSize(out_cc, &case_env, data).c_str());
}
if ( type_->incremental_input() ) {
ASSERT(case_type()->parsing_complete_var());
out_cc->println("%s = %s;", case_env.LValue(case_type()->parsing_complete_var()),
case_env.RValue(type_->parsing_complete_var()));
}
}
out_cc->dec_indent();
out_cc->println("}");
out_cc->println("break;");
out_cc->dec_indent();
}
bool CaseField::DoTraverse(DataDepVisitor* visitor) { return Field::DoTraverse(visitor) && type()->Traverse(visitor); }
bool CaseField::RequiresAnalyzerContext() const {
return Field::RequiresAnalyzerContext() || type()->RequiresAnalyzerContext();
}

View file

@ -1,100 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_case_h
#define pac_case_h
#include "pac_common.h"
#include "pac_field.h"
#include "pac_id.h"
#include "pac_type.h"
class CaseType : public Type {
public:
CaseType(Expr* index, CaseFieldList* cases);
~CaseType() override;
void AddCaseField(CaseField* f);
bool DefineValueVar() const override;
string DataTypeStr() const override;
string DefaultValue() const override;
void Prepare(Env* env, int flags) override;
void GenPubDecls(Output* out, Env* env) override;
void GenPrivDecls(Output* out, Env* env) override;
void GenInitCode(Output* out, Env* env) override;
void GenCleanUpCode(Output* out, Env* env) override;
int StaticSize(Env* env) const override;
void SetBoundaryChecked() override;
Type* ValueType() const;
Expr* IndexExpr() const { return index_expr_; }
bool IsPointerType() const override { return ValueType()->IsPointerType(); }
protected:
void DoGenParseCode(Output* out, Env* env, const DataPtr& data, int flags) override;
void GenDynamicSize(Output* out, Env* env, const DataPtr& data) override;
Type* DoClone() const override { return nullptr; }
void DoMarkIncrementalInput() override;
bool ByteOrderSensitive() const override;
Expr* index_expr_;
ID* index_var_;
CaseFieldList* cases_;
typedef map<const ID*, CaseField*, ID_ptr_cmp> member_map_t;
member_map_t member_map_;
};
class CaseField : public Field {
public:
CaseField(ExprList* index, ID* id, Type* type);
~CaseField() override;
CaseType* case_type() const { return case_type_; }
void set_case_type(CaseType* t) { case_type_ = t; }
ExprList* index() const { return index_; }
const char* lvalue() const { return type_->lvalue(); }
const char* CaseStr(Env* env);
void set_index_var(const ID* var) { index_var_ = var; }
void Prepare(Env* env) override;
void GenPubDecls(Output* out, Env* env) override;
void GenInitCode(Output* out, Env* env) override;
void GenCleanUpCode(Output* out, Env* env) override;
void GenParseCode(Output* out, Env* env, const DataPtr& data, const ID* size_var);
int StaticSize(Env* env) const { return type_->StaticSize(env); }
bool IsDefaultCase() const { return ! index_; }
void SetBoundaryChecked() { type_->SetBoundaryChecked(); }
bool RequiresByteOrder() const { return type_->RequiresByteOrder(); }
bool RequiresAnalyzerContext() const override;
protected:
bool DoTraverse(DataDepVisitor* visitor) override;
protected:
CaseType* case_type_;
ExprList* index_;
const ID* index_var_;
};
// Generate a list of "case X:" lines from index_list. Each index
// expression must be constant foldable.
void GenCaseStr(ExprList* index_list, Output* out_cc, Env* env, Type* switch_type);
#endif // pac_case_h

View file

@ -1,79 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_cclass_h
#define pac_cclass_h
class CClass;
class CClassMember;
class CClassMethod;
class CType;
class CVariable;
typedef vector<CClassMember*> CClassMemberList;
typedef vector<CClassMethod*> CClassMethodList;
typedef vector<CVariable*> CVariableList;
#include "pac_common.h"
// Represents a C++ class.
//
// For now we adopt a simple model:
//
// 1. All members have a protected member variable "name_" and a
// public constant access method "name()".
//
// 2. All methods are public.
//
// 3. We do not check repeated names.
class CClass {
public:
CClass(const string& class_name);
void AddMember(CClassMember* member);
void AddMethod(CClassMember* method);
void GenForwardDeclaration(Output* out_h);
void GenCode(Output* out_h, Output* out_cc);
protected:
string class_name_;
CClassMemberList* members_;
CClassMethodList* methods_;
};
class CVariable {
public:
CClassMember(const string& name, CType* type);
string name() const { return name_; }
CType* type() const { return type_; }
protected:
string name_;
CType* type_;
};
class CClassMember {
public:
CClassMember(CVariable* var);
void GenCode(Output* out_h, Output* out_cc);
string decl() const;
protected:
CVariable* var_;
};
class CClassMethod {
public:
CClassMethod(CVariable* var, CVariableList* params);
string decl() const;
protected:
CVariable* var_;
CVariableList* params_;
};
#endif // pac_cclass_h

View file

@ -1,133 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_common_h
#define pac_common_h
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include "pac_utils.h"
using namespace std;
extern bool FLAGS_pac_debug;
extern bool FLAGS_quiet;
extern vector<string> FLAGS_include_directories;
extern string input_filename;
extern int line_number;
// Definition of class Object, which is the base class for all objects
// representing language elements -- identifiers, types, expressions,
// etc.
class Object {
public:
Object() {
filename = input_filename;
line_num = line_number;
location = strfmt("%s:%d", filename.c_str(), line_number);
}
~Object() {}
const char* Location() const { return location.c_str(); }
protected:
string filename;
int line_num;
string location;
};
class ActionParam;
class ActionParamType;
class AnalyzerAction;
class AnalyzerContextDecl;
class AnalyzerDecl;
class AnalyzerElement;
class ArrayType;
class Attr;
class CClass;
class CType;
class ConstString;
class CaseExpr;
class CaseField;
class ContextField;
class DataPtr;
class Decl;
class EmbeddedCode;
class Enum;
class Env;
class ExternType;
class Expr;
class Field;
class Function;
class InputBuffer;
class LetDef;
class LetField;
class ID;
class Nullptr;
class Number;
class Output;
class PacPrimitive;
class Param;
class ParameterizedType;
class RecordType;
class RecordField;
class RecordDataField;
class RecordPaddingField;
class RegEx;
class SeqEnd;
class StateVar;
class Type;
class TypeDecl;
class WithInputField;
// The ID of the current declaration.
extern const ID* current_decl_id;
typedef vector<ActionParam*> ActionParamList;
typedef vector<AnalyzerAction*> AnalyzerActionList;
typedef vector<AnalyzerElement*> AnalyzerElementList;
typedef vector<Attr*> AttrList;
typedef vector<CaseExpr*> CaseExprList;
typedef vector<CaseField*> CaseFieldList;
typedef vector<ContextField*> ContextFieldList;
typedef vector<Decl*> DeclList;
typedef vector<Enum*> EnumList;
typedef vector<Expr*> ExprList;
typedef vector<Field*> FieldList;
typedef vector<LetField*> LetFieldList;
typedef vector<Number*> NumList;
typedef vector<Param*> ParamList;
typedef vector<RecordField*> RecordFieldList;
typedef vector<StateVar*> StateVarList;
#define foreach(i, ct, pc) \
if ( pc ) \
for ( ct::iterator i = (pc)->begin(); i != (pc)->end(); ++i )
#define delete_list(ct, pc) \
{ \
foreach (delete_list_i, ct, pc) \
delete *delete_list_i; \
delete pc; \
pc = 0; \
}
// Constants
const char* const kComputeFrameLength = "compute_frame_length";
const char* const kFlowBufferClass = "FlowBuffer";
const char* const kFlowBufferVar = "flow_buffer";
const char* const kFlowEOF = "FlowEOF";
const char* const kFlowGap = "NewGap";
const char* const kInitialBufferLengthFunc = "initial_buffer_length";
const char* const kNeedMoreData = "need_more_data";
const char* const kNewData = "NewData";
const char* const kParseFuncWithBuffer = "ParseBuffer";
const char* const kParseFuncWithoutBuffer = "Parse";
const char* const kRefCountClass = "binpac::RefCount";
const char* const kTypeWithLengthClass = "binpac::TypeWithLength";
#endif // pac_common_h

View file

@ -1,132 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_conn.h"
#include "pac_analyzer.h"
#include "pac_dataunit.h"
#include "pac_embedded.h"
#include "pac_exception.h"
#include "pac_expr.h"
#include "pac_flow.h"
#include "pac_output.h"
#include "pac_paramtype.h"
#include "pac_type.h"
ConnDecl::ConnDecl(ID* conn_id, ParamList* params, AnalyzerElementList* elemlist)
: AnalyzerDecl(conn_id, CONN, params) {
flows_[0] = flows_[1] = nullptr;
AddElements(elemlist);
data_type_ = new ParameterizedType(conn_id->clone(), nullptr);
}
ConnDecl::~ConnDecl() {
delete flows_[0];
delete flows_[1];
delete data_type_;
}
void ConnDecl::AddBaseClass(vector<string>* base_classes) const {
base_classes->push_back("binpac::ConnectionAnalyzer");
}
void ConnDecl::ProcessFlowElement(AnalyzerFlow* flow_elem) {
int flow_index;
if ( flow_elem->dir() == AnalyzerFlow::UP )
flow_index = 0;
else
flow_index = 1;
if ( flows_[flow_index] ) {
throw Exception(flow_elem, strfmt("%sflow already defined", flow_index == 0 ? "up" : "down"));
}
flows_[flow_index] = flow_elem;
type_->AddField(flow_elem->flow_field());
}
void ConnDecl::ProcessDataUnitElement(AnalyzerDataUnit* dataunit_elem) {
throw Exception(dataunit_elem, "dataunit should be defined in only a flow declaration");
}
void ConnDecl::Prepare() {
AnalyzerDecl::Prepare();
flows_[0]->flow_decl()->set_conn_decl(this);
flows_[1]->flow_decl()->set_conn_decl(this);
}
void ConnDecl::GenPubDecls(Output* out_h, Output* out_cc) { AnalyzerDecl::GenPubDecls(out_h, out_cc); }
void ConnDecl::GenPrivDecls(Output* out_h, Output* out_cc) { AnalyzerDecl::GenPrivDecls(out_h, out_cc); }
void ConnDecl::GenEOFFunc(Output* out_h, Output* out_cc) {
string proto = strfmt("%s(bool is_orig)", kFlowEOF);
out_h->println("void %s;", proto.c_str());
out_cc->println("void %s::%s {", class_name().c_str(), proto.c_str());
out_cc->inc_indent();
out_cc->println("if ( is_orig )");
out_cc->inc_indent();
out_cc->println("%s->%s();", env_->LValue(upflow_id), kFlowEOF);
out_cc->dec_indent();
out_cc->println("else");
out_cc->inc_indent();
out_cc->println("%s->%s();", env_->LValue(downflow_id), kFlowEOF);
foreach (i, AnalyzerHelperList, eof_helpers_) {
(*i)->GenCode(nullptr, out_cc, this);
}
out_cc->dec_indent();
out_cc->dec_indent();
out_cc->println("}");
out_cc->println("");
}
void ConnDecl::GenGapFunc(Output* out_h, Output* out_cc) {
string proto = strfmt("%s(bool is_orig, int gap_length)", kFlowGap);
out_h->println("void %s;", proto.c_str());
out_cc->println("void %s::%s {", class_name().c_str(), proto.c_str());
out_cc->inc_indent();
out_cc->println("if ( is_orig )");
out_cc->inc_indent();
out_cc->println("%s->%s(gap_length);", env_->LValue(upflow_id), kFlowGap);
out_cc->dec_indent();
out_cc->println("else");
out_cc->inc_indent();
out_cc->println("%s->%s(gap_length);", env_->LValue(downflow_id), kFlowGap);
out_cc->dec_indent();
out_cc->dec_indent();
out_cc->println("}");
out_cc->println("");
}
void ConnDecl::GenProcessFunc(Output* out_h, Output* out_cc) {
string proto = strfmt("%s(bool is_orig, const_byteptr begin, const_byteptr end)", kNewData);
out_h->println("void %s override;", proto.c_str());
out_cc->println("void %s::%s {", class_name().c_str(), proto.c_str());
out_cc->inc_indent();
out_cc->println("if ( is_orig )");
out_cc->inc_indent();
out_cc->println("%s->%s(begin, end);", env_->LValue(upflow_id), kNewData);
out_cc->dec_indent();
out_cc->println("else");
out_cc->inc_indent();
out_cc->println("%s->%s(begin, end);", env_->LValue(downflow_id), kNewData);
out_cc->dec_indent();
out_cc->dec_indent();
out_cc->println("}");
out_cc->println("");
}

View file

@ -1,35 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_conn_h
#define pac_conn_h
#include "pac_analyzer.h"
#include "pac_decl.h"
class ConnDecl : public AnalyzerDecl {
public:
ConnDecl(ID* conn_id, ParamList* params, AnalyzerElementList* elemlist);
~ConnDecl() override;
void Prepare() override;
Type* DataType() const { return data_type_; }
protected:
void AddBaseClass(vector<string>* base_classes) const override;
void GenProcessFunc(Output* out_h, Output* out_cc) override;
void GenGapFunc(Output* out_h, Output* out_cc) override;
void GenEOFFunc(Output* out_h, Output* out_cc) override;
void GenPubDecls(Output* out_h, Output* out_cc) override;
void GenPrivDecls(Output* out_h, Output* out_cc) override;
void ProcessFlowElement(AnalyzerFlow* flow_elem) override;
void ProcessDataUnitElement(AnalyzerDataUnit* dataunit_elem) override;
AnalyzerFlow* flows_[2];
Type* data_type_;
};
#endif // pac_conn_h

View file

@ -1,96 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_context.h"
#include "pac_analyzer.h"
#include "pac_exception.h"
#include "pac_exttype.h"
#include "pac_flow.h"
#include "pac_id.h"
#include "pac_output.h"
#include "pac_param.h"
#include "pac_paramtype.h"
#include "pac_type.h"
#include "pac_utils.h"
ContextField::ContextField(ID* id, Type* type)
: Field(CONTEXT_FIELD, TYPE_NOT_TO_BE_PARSED | CLASS_MEMBER | PUBLIC_READABLE, id, type) {}
AnalyzerContextDecl* AnalyzerContextDecl::current_analyzer_context_ = nullptr;
namespace {
ParamList* ContextFieldsToParams(ContextFieldList* context_fields) {
// Convert context fields to parameters
ParamList* params = new ParamList();
foreach (i, ContextFieldList, context_fields) {
ContextField* f = *i;
params->push_back(new Param(f->id()->clone(), f->type()));
}
return params;
}
} // namespace
AnalyzerContextDecl::AnalyzerContextDecl(ID* id, ContextFieldList* context_fields)
: TypeDecl(new ID(strfmt("Context%s", id->Name())), ContextFieldsToParams(context_fields), new DummyType()) {
context_name_id_ = id;
if ( current_analyzer_context_ != nullptr ) {
throw Exception(this, strfmt("multiple declaration of analyzer context; "
"the previous one is `%s'",
current_analyzer_context_->id()->Name()));
}
else
current_analyzer_context_ = this;
context_fields_ = context_fields;
param_type_ = new ParameterizedType(id_->clone(), nullptr);
flow_buffer_added_ = false;
DEBUG_MSG("Context type: %s\n", param_type()->class_name().c_str());
}
AnalyzerContextDecl::~AnalyzerContextDecl() {
delete context_name_id_;
delete param_type_;
delete_list(ContextFieldList, context_fields_);
}
void AnalyzerContextDecl::GenForwardDeclaration(Output* out_h) {
GenNamespaceBegin(out_h);
TypeDecl::GenForwardDeclaration(out_h);
}
void AnalyzerContextDecl::GenCode(Output* out_h, Output* out_cc) {
GenNamespaceBegin(out_h);
GenNamespaceBegin(out_cc);
TypeDecl::GenCode(out_h, out_cc);
}
void AnalyzerContextDecl::GenNamespaceBegin(Output* out) const {
out->println("namespace %s {", context_name_id()->Name());
}
void AnalyzerContextDecl::GenNamespaceEnd(Output* out) const {
out->println("} // namespace %s", context_name_id()->Name());
}
void AnalyzerContextDecl::AddFlowBuffer() {
if ( flow_buffer_added_ )
return;
AddParam(new Param(new ID(kFlowBufferVar), FlowDecl::flow_buffer_type()->Clone()));
flow_buffer_added_ = true;
}
string AnalyzerContextDecl::mb_buffer(Env* env) {
// A hack. The orthodox way would be to build an Expr of
// context.flow_buffer_var, and then EvalExpr.
return strfmt("%s->%s()", env->RValue(analyzer_context_id), kFlowBufferVar);
}
Type* DummyType::DoClone() const {
// Fields will be copied in Type::Clone().
return new DummyType();
}

View file

@ -1,99 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_context_h
#define pac_context_h
#include "pac_common.h"
#include "pac_field.h"
#include "pac_type.h"
#include "pac_typedecl.h"
// AnalyzerContext represents a cookie that an analyzer gives to
// parse functions of various message types. The cookie is parsed
// to every parse function (if necessary) as parameter 'binpac_context'.
//
// The members of the cookie is declared through 'analyzer' declarations,
// such as in:
//
// analyzer SunRPC withcontext {
// connection: RPC_Conn;
// flow: RPC_Flow;
// };
//
// The cookie usually contains the connection and flow in which
// the message appears, and the context information can be
// accessed as members of the cookie, such as
// ``binpac_context.connection''.
class ContextField : public Field {
public:
ContextField(ID* id, Type* type);
};
class AnalyzerContextDecl : public TypeDecl {
public:
AnalyzerContextDecl(ID* id, ContextFieldList* context_fields);
~AnalyzerContextDecl() override;
void AddFlowBuffer();
const ID* context_name_id() const { return context_name_id_; }
// The type of analyzer context as a parameter
ParameterizedType* param_type() const { return param_type_; }
void GenForwardDeclaration(Output* out_h) override;
void GenCode(Output* out_h, Output* out_cc) override;
void GenNamespaceBegin(Output* out) const;
void GenNamespaceEnd(Output* out) const;
private:
ID* context_name_id_;
ContextFieldList* context_fields_;
ParameterizedType* param_type_;
bool flow_buffer_added_;
// static members
public:
static AnalyzerContextDecl* current_analyzer_context() { return current_analyzer_context_; }
static string mb_buffer(Env* env);
private:
static AnalyzerContextDecl* current_analyzer_context_;
};
class DummyType : public Type {
public:
DummyType() : Type(DUMMY) {}
bool DefineValueVar() const override { return false; }
string DataTypeStr() const override {
ASSERT(0);
return "";
}
int StaticSize(Env* env) const override {
ASSERT(0);
return -1;
}
bool ByteOrderSensitive() const override { return false; }
bool IsPointerType() const override {
ASSERT(0);
return false;
}
void DoGenParseCode(Output* out, Env* env, const DataPtr& data, int flags) override { ASSERT(0); }
// Generate code for computing the dynamic size of the type
void GenDynamicSize(Output* out, Env* env, const DataPtr& data) override { ASSERT(0); }
protected:
Type* DoClone() const override;
void DoMarkIncrementalInput() override { ASSERT(0); }
};
#endif // pac_context_h

View file

@ -1,112 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_cstr.h"
#include "pac_dbg.h"
#include "pac_exception.h"
namespace {
class EscapeException {
public:
explicit EscapeException(const string& s) { msg_ = s; }
const string& msg() const { return msg_; }
private:
string msg_;
};
// Copied from util.cc of Zeek
int expand_escape(const char*& s) {
switch ( *(s++) ) {
case 'b': return '\b';
case 'f': return '\f';
case 'n': return '\n';
case 'r': return '\r';
case 't': return '\t';
case 'a': return '\a';
case 'v': return '\v';
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7': { // \<octal>{1,3}
--s; // put back the first octal digit
const char* start = s;
// Don't increment inside loop control
// because if isdigit() is a macro it might
// expand into multiple increments ...
// Here we define a maximum length for escape sequence
// to allow easy handling of string like: "^H0" as
// "\0100".
for ( int len = 0; len < 3 && isascii(*s) && isdigit(*s); ++s, ++len )
;
int result;
if ( sscanf(start, "%3o", &result) != 1 )
throw EscapeException(strfmt("bad octal escape: \"%s", start));
return result;
}
case 'x': { /* \x<hex> */
const char* start = s;
// Look at most 2 characters, so that "\x0ddir" -> "^Mdir".
for ( int len = 0; len < 2 && isascii(*s) && isxdigit(*s); ++s, ++len )
;
int result;
if ( sscanf(start, "%2x", &result) != 1 )
throw EscapeException(strfmt("bad hexadecimal escape: \"%s", start));
return result;
}
default: return s[-1];
}
}
} // namespace
ConstString::ConstString(const string& s) : str_(s) {
// Copied from scan.l of Zeek
try {
const char* text = str_.c_str();
int len = strlen(text) + 1;
int i = 0;
char* new_s = new char[len];
// Skip leading quote.
for ( ++text; *text; ++text ) {
if ( *text == '\\' ) {
++text; // skip '\'
new_s[i++] = expand_escape(text);
--text; // point to end of sequence
}
else {
new_s[i++] = *text;
}
}
ASSERT(i < len);
// Get rid of trailing quote.
ASSERT(new_s[i - 1] == '"');
new_s[i - 1] = '\0';
unescaped_ = new_s;
delete[] new_s;
} catch ( EscapeException const& e ) {
// Throw again with the object
throw Exception(this, e.msg().c_str());
}
}

View file

@ -1,24 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_cstr_h
#define pac_cstr_h
#include "pac_common.h"
class ConstString : public Object {
public:
ConstString(const string& s);
// The string in its escaped form, with surrounding '"'s
const string& str() const { return str_; }
const char* c_str() const { return str_.c_str(); }
// The unescaped string, without surrounding '"'s
const string& unescaped() const { return unescaped_; }
private:
string str_;
string unescaped_;
};
#endif // pac_cstr_h

View file

@ -1,15 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_ctype.h"
string CType::DeclareInstance(const string& var) const { return strfmt("%s %s", name().c_str(), var.c_str()); }
string CType::DeclareConstReference(const string& var) const {
return strfmt("%s const& %s", name().c_str(), var.c_str());
}
string CType::DeclareConstPointer(const string& var) const {
return strfmt("%s const* %s", name().c_str(), var.c_str());
}
string CType::DeclarePointer(const string& var) const { return strfmt("%s* %s", name().c_str(), var.c_str()); }

View file

@ -1,24 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_ctype_h
#define pac_ctype_h
#include "pac_common.h"
// Represents a C++ type
class CType {
public:
CType(const string& name);
string name() const { return name_; }
string DeclareInstance(const string& var) const;
string DeclareConstReference(const string& var) const;
string DeclareConstPointer(const string& var) const;
string DeclarePointer(const string& var) const;
protected:
string name_;
};
#endif // pac_ctype_h

View file

@ -1,58 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_datadep.h"
#include "pac_expr.h"
#include "pac_id.h"
#include "pac_type.h"
DataDepElement::DataDepElement(DDE_Type type) : dde_type_(type), in_traversal(false) {}
bool DataDepElement::Traverse(DataDepVisitor* visitor) {
// Avoid infinite loop
if ( in_traversal )
return true;
if ( ! visitor->PreProcess(this) )
return false;
in_traversal = true;
bool cont = DoTraverse(visitor);
in_traversal = false;
if ( ! cont )
return false;
if ( ! visitor->PostProcess(this) )
return false;
return true;
}
Expr* DataDepElement::expr() { return static_cast<Expr*>(this); }
Type* DataDepElement::type() { return static_cast<Type*>(this); }
bool RequiresAnalyzerContext::PreProcess(DataDepElement* element) {
switch ( element->dde_type() ) {
case DataDepElement::EXPR: ProcessExpr(element->expr()); break;
default: break;
}
// Continue traversal until we know the answer is 'yes'
return ! requires_analyzer_context_;
}
bool RequiresAnalyzerContext::PostProcess(DataDepElement* element) { return ! requires_analyzer_context_; }
void RequiresAnalyzerContext::ProcessExpr(Expr* expr) {
if ( expr->expr_type() == Expr::EXPR_ID ) {
requires_analyzer_context_ =
(requires_analyzer_context_ || *expr->id() == *analyzer_context_id || *expr->id() == *context_macro_id);
}
}
bool RequiresAnalyzerContext::compute(DataDepElement* element) {
RequiresAnalyzerContext visitor;
// This result is intentionally ignored. We want to traverse, but always return
// the same result.
std::ignore = element->Traverse(&visitor);
return visitor.requires_analyzer_context_;
}

View file

@ -1,70 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_datadep_h
#define pac_datadep_h
// To provide a way to traverse through the data dependency graph.
// That is, to evaluate X, what must be evaluated.
#include "pac_common.h"
#include "pac_dbg.h"
class DataDepVisitor;
class DataDepElement {
public:
enum DDE_Type {
ATTR,
CASEEXPR,
EXPR,
FIELD,
INPUT_BUFFER,
PARAM,
TYPE,
};
DataDepElement(DDE_Type type);
virtual ~DataDepElement() {}
// Returns whether to continue traversal
bool Traverse(DataDepVisitor* visitor);
// Returns whether to continue traversal
virtual bool DoTraverse(DataDepVisitor* visitor) = 0;
DDE_Type dde_type() const { return dde_type_; }
Expr* expr();
Type* type();
protected:
DDE_Type dde_type_;
bool in_traversal;
};
class DataDepVisitor {
public:
virtual ~DataDepVisitor() {}
// Returns whether to continue traversal
virtual bool PreProcess(DataDepElement* element) = 0;
virtual bool PostProcess(DataDepElement* element) = 0;
};
class RequiresAnalyzerContext : public DataDepVisitor {
public:
RequiresAnalyzerContext() : requires_analyzer_context_(false) {}
// Returns whether to continue traversal
bool PreProcess(DataDepElement* element) override;
bool PostProcess(DataDepElement* element) override;
bool requires_analyzer_context() const { return requires_analyzer_context_; }
static bool compute(DataDepElement* element);
protected:
void ProcessExpr(Expr* expr);
bool requires_analyzer_context_;
};
#endif // pac_datadep_h

View file

@ -1,53 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_dataptr.h"
#include "pac_exception.h"
#include "pac_id.h"
#include "pac_output.h"
#include "pac_utils.h"
DataPtr::DataPtr(Env* env, const ID* id, const int offset) : id_(id), offset_(offset) {
if ( id_ ) {
if ( ! env->Evaluated(id_) )
throw ExceptionIDNotEvaluated(id_);
if ( offset_ == 0 )
ptr_expr_ = strfmt("%s", env->RValue(id_));
else
ptr_expr_ = strfmt("(%s + %d)", env->RValue(id_), offset_);
}
else
ptr_expr_ = "(null id)";
}
int DataPtr::AbsOffset(const ID* base_ptr) const { return (id() == base_ptr) ? offset() : -1; }
char* DataPtr::AbsOffsetExpr(Env* env, const ID* base_ptr) const {
if ( AbsOffset(base_ptr) >= 0 )
return nfmt("%d", offset());
else
return nfmt("(%s - %s)", ptr_expr(), env->RValue(base_ptr));
}
void DataPtr::GenBoundaryCheck(Output* out_cc, Env* env, const char* data_size, const char* data_name) const {
ASSERT(id_);
out_cc->println("// Checking out-of-bound for \"%s\"", data_name);
out_cc->println("if ( %s + (%s) > %s || %s + (%s) < %s ) {", ptr_expr(), data_size, env->RValue(end_of_data),
ptr_expr(), data_size, ptr_expr());
out_cc->inc_indent();
char* data_offset = AbsOffsetExpr(env, begin_of_data);
out_cc->println("// Handle out-of-bound condition");
out_cc->println("throw binpac::ExceptionOutOfBound(\"%s\",", data_name);
out_cc->println(" (%s) + (%s), ", data_offset, data_size);
out_cc->println(" (%s) - (%s));", env->RValue(end_of_data), env->RValue(begin_of_data));
delete[] data_offset;
out_cc->dec_indent();
out_cc->println("}");
}

View file

@ -1,46 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_dataptr_h
#define pac_dataptr_h
#include <string>
#include "pac_common.h"
#include "pac_dbg.h"
// A data pointer is represented by an data pointer variable
// plus a constant offset.
class DataPtr {
public:
DataPtr(Env* env, const ID* arg_id, const int arg_off);
DataPtr(DataPtr const& x) { *this = x; }
DataPtr const& operator=(DataPtr const& x) {
id_ = x.id();
offset_ = x.offset();
ptr_expr_ = x.ptr_expr();
return *this;
}
const ID* id() const { return id_; }
int offset() const { return offset_; }
const char* ptr_expr() const {
ASSERT(id_);
return ptr_expr_.c_str();
}
int AbsOffset(const ID* base_ptr) const;
char* AbsOffsetExpr(Env* env, const ID* base_ptr) const;
void GenBoundaryCheck(Output* out, Env* env, const char* data_size, const char* data_name) const;
protected:
const ID* id_;
int offset_;
string ptr_expr_;
};
#endif // pac_dataptr_h

View file

@ -1,39 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_dataunit.h"
#include "pac_context.h"
#include "pac_output.h"
#include "pac_paramtype.h"
#include "pac_varfield.h"
AnalyzerDataUnit::AnalyzerDataUnit(DataUnitType type, ID* id, ExprList* type_params, ExprList* context_params)
: AnalyzerElement(DATAUNIT), type_(type), id_(id), type_params_(type_params), context_params_(context_params) {
data_type_ = new ParameterizedType(id_, type_params_);
context_type_ =
new ParameterizedType(AnalyzerContextDecl::current_analyzer_context()->id()->clone(), context_params_);
dataunit_var_field_ = new ParseVarField(Field::CLASS_MEMBER, dataunit_id->clone(), data_type());
context_var_field_ = new PrivVarField(analyzer_context_id->clone(), context_type());
}
AnalyzerDataUnit::~AnalyzerDataUnit() {
delete dataunit_var_field_;
delete context_var_field_;
}
void AnalyzerDataUnit::Prepare(Env* env) {
dataunit_var_field_->Prepare(env);
context_var_field_->Prepare(env);
}
void AnalyzerDataUnit::GenNewDataUnit(Output* out_cc, Env* env) {
out_cc->println("%s = new %s(%s);", env->LValue(dataunit_id), data_type()->class_name().c_str(),
data_type()->EvalParameters(out_cc, env).c_str());
}
void AnalyzerDataUnit::GenNewContext(Output* out_cc, Env* env) {
out_cc->println("%s = new %s(%s);", env->LValue(analyzer_context_id), context_type()->class_name().c_str(),
context_type()->EvalParameters(out_cc, env).c_str());
env->SetEvaluated(analyzer_context_id);
}

View file

@ -1,46 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_dataunit_h
#define pac_dataunit_h
#include "pac_analyzer.h"
// The type and parameters of input data unit of a flow. For instance, the
// data unit of a DCE/RPC flow is DCE_RPC_PDU.
class AnalyzerDataUnit : public AnalyzerElement {
public:
enum DataUnitType { DATAGRAM, FLOWUNIT };
AnalyzerDataUnit(DataUnitType type, ID* id, ExprList* type_params, ExprList* context_params);
~AnalyzerDataUnit() override;
void Prepare(Env* env);
// Initializes dataunit_id
void GenNewDataUnit(Output* out_cc, Env* env);
// Initializes analyzer_context_id
void GenNewContext(Output* out_cc, Env* env);
DataUnitType type() const { return type_; }
const ID* id() const { return id_; }
ExprList* type_params() const { return type_params_; }
ExprList* context_params() const { return context_params_; }
ParameterizedType* data_type() const { return data_type_; }
ParameterizedType* context_type() const { return context_type_; }
Field* dataunit_var_field() const { return dataunit_var_field_; }
Field* context_var_field() const { return context_var_field_; }
private:
DataUnitType type_;
ID* id_;
ExprList* type_params_;
ExprList* context_params_;
ParameterizedType* data_type_;
ParameterizedType* context_type_;
Field* dataunit_var_field_;
Field* context_var_field_;
};
#endif // pac_dataunit_h

View file

@ -1,16 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_dbg_h
#define pac_dbg_h
#include <assert.h>
#include <stdio.h>
extern bool FLAGS_pac_debug;
#define ASSERT(x) assert(x)
#define DEBUG_MSG(...) \
if ( FLAGS_pac_debug ) \
fprintf(stderr, __VA_ARGS__)
#endif /* pac_dbg_h */

View file

@ -1,8 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_decl_inl_h
#define pac_decl_inl_h
#include "pac_id.h"
#endif // pac_decl_inl_h

View file

@ -1,165 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_decl.h"
#include "pac_attr.h"
#include "pac_context.h"
#include "pac_dataptr.h"
#include "pac_embedded.h"
#include "pac_exception.h"
#include "pac_expr.h"
#include "pac_exttype.h"
#include "pac_id.h"
#include "pac_output.h"
#include "pac_param.h"
#include "pac_record.h"
#include "pac_type.h"
#include "pac_utils.h"
DeclList* Decl::decl_list_ = nullptr;
Decl::DeclMap Decl::decl_map_;
Decl::Decl(ID* id, DeclType decl_type) : id_(id), decl_type_(decl_type), attrlist_(nullptr) {
decl_map_[id_] = this;
if ( ! decl_list_ )
decl_list_ = new DeclList();
decl_list_->push_back(this);
DEBUG_MSG("Finished Decl %s\n", id_->Name());
analyzer_context_ = nullptr;
}
Decl::~Decl() {
delete id_;
delete_list(AttrList, attrlist_);
}
void Decl::AddAttrs(AttrList* attrs) {
if ( ! attrs )
return;
if ( ! attrlist_ )
attrlist_ = new AttrList();
foreach (i, AttrList, attrs) {
attrlist_->push_back(*i);
ProcessAttr(*i);
}
}
void Decl::ProcessAttr(Attr* attr) { throw Exception(attr, "unhandled attribute"); }
void Decl::SetAnalyzerContext() {
analyzer_context_ = AnalyzerContextDecl::current_analyzer_context();
if ( ! analyzer_context_ ) {
throw Exception(this, "analyzer context not defined");
}
}
void Decl::ProcessDecls(Output* out_h, Output* out_cc) {
if ( ! decl_list_ )
return;
foreach (i, DeclList, decl_list_) {
Decl* decl = *i;
current_decl_id = decl->id();
decl->Prepare();
}
foreach (i, DeclList, decl_list_) {
Decl* decl = *i;
current_decl_id = decl->id();
decl->GenExternDeclaration(out_h);
}
out_h->println("namespace binpac {\n");
out_cc->println("namespace binpac {\n");
AnalyzerContextDecl* analyzer_context = AnalyzerContextDecl::current_analyzer_context();
foreach (i, DeclList, decl_list_) {
Decl* decl = *i;
current_decl_id = decl->id();
decl->GenForwardDeclaration(out_h);
}
if ( analyzer_context )
analyzer_context->GenNamespaceEnd(out_h);
out_h->println("");
foreach (i, DeclList, decl_list_) {
Decl* decl = *i;
current_decl_id = decl->id();
decl->GenCode(out_h, out_cc);
}
if ( analyzer_context ) {
analyzer_context->GenNamespaceEnd(out_h);
analyzer_context->GenNamespaceEnd(out_cc);
}
out_h->println("} // namespace binpac");
out_cc->println("} // namespace binpac");
}
Decl* Decl::LookUpDecl(const ID* id) {
DeclMap::iterator it = decl_map_.find(id);
if ( it == decl_map_.end() )
return nullptr;
return it->second;
}
int HelperDecl::helper_id_seq = 0;
HelperDecl::HelperDecl(HelperType helper_type, ID* context_id, EmbeddedCode* code)
: Decl(new ID(strfmt("helper_%d", ++helper_id_seq)), HELPER),
helper_type_(helper_type),
context_id_(context_id),
code_(code) {}
HelperDecl::~HelperDecl() {
delete context_id_;
delete code_;
}
void HelperDecl::Prepare() {
// Do nothing
}
void HelperDecl::GenExternDeclaration(Output* out_h) {
if ( helper_type_ == EXTERN )
code_->GenCode(out_h, global_env());
}
void HelperDecl::GenCode(Output* out_h, Output* out_cc) {
Env* env = global_env();
#if 0
if ( context_id_ )
{
Decl *decl = Decl::LookUpDecl(context_id_);
if ( ! decl )
{
throw Exception(context_id_,
fmt("cannot find declaration for %s",
context_id_->Name()));
}
env = decl->env();
if ( ! env )
{
throw Exception(context_id_,
fmt("not a type or analyzer: %s",
context_id_->Name()));
}
}
#endif
if ( helper_type_ == HEADER )
code_->GenCode(out_h, env);
else if ( helper_type_ == CODE )
code_->GenCode(out_cc, env);
else if ( helper_type_ == EXTERN )
; // do nothing
else
ASSERT(0);
}

View file

@ -1,80 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_decl_h
#define pac_decl_h
#include "pac_common.h"
#include "pac_id.h"
class Decl : public Object {
public:
// Note: ANALYZER is not for AnalyzerDecl (which is an
// abstract class) , but for AnalyzerContextDecl.
enum DeclType { ENUM, LET, TYPE, FUNC, CONN, FLOW, ANALYZER, HELPER, REGEX };
Decl(ID* id, DeclType decl_type);
virtual ~Decl();
const ID* id() const { return id_; }
DeclType decl_type() const { return decl_type_; }
AnalyzerContextDecl* analyzer_context() const { return analyzer_context_; }
// NULL except for TypeDecl or AnalyzerDecl
virtual Env* env() const { return nullptr; }
virtual void Prepare() = 0;
// Generate declarations out of the "binpac" namespace
virtual void GenExternDeclaration(Output* out_h) { /* do nothing */ }
// Generate declarations before definition of classes
virtual void GenForwardDeclaration(Output* out_h) = 0;
virtual void GenCode(Output* out_h, Output* out_cc) = 0;
void TakeExprList();
void AddAttrs(AttrList* attrlist);
void SetAnalyzerContext();
protected:
virtual void ProcessAttr(Attr* a);
ID* id_;
DeclType decl_type_;
AttrList* attrlist_;
AnalyzerContextDecl* analyzer_context_;
public:
static void ProcessDecls(Output* out_h, Output* out_cc);
static Decl* LookUpDecl(const ID* id);
private:
static DeclList* decl_list_;
typedef map<const ID*, Decl*, ID_ptr_cmp> DeclMap;
static DeclMap decl_map_;
};
class HelperDecl : public Decl {
public:
enum HelperType {
HEADER,
CODE,
EXTERN,
};
HelperDecl(HelperType type, ID* context_id, EmbeddedCode* code);
~HelperDecl() override;
void Prepare() override;
void GenExternDeclaration(Output* out_h) override;
void GenForwardDeclaration(Output* out_h) override { /* do nothing */ }
void GenCode(Output* out_h, Output* out_cc) override;
private:
HelperType helper_type_;
ID* context_id_;
EmbeddedCode* code_;
static int helper_id_seq;
};
#endif // pac_decl_h

View file

@ -1,57 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_embedded.h"
#include "pac_id.h"
#include "pac_output.h"
#include "pac_primitive.h"
EmbeddedCodeSegment::EmbeddedCodeSegment(const string& s) : s_(s), primitive_(nullptr) {}
EmbeddedCodeSegment::EmbeddedCodeSegment(PacPrimitive* primitive) : s_(""), primitive_(primitive) {}
EmbeddedCodeSegment::~EmbeddedCodeSegment() { delete primitive_; }
string EmbeddedCodeSegment::ToCode(Env* env) {
if ( primitive_ && s_.empty() )
s_ = primitive_->ToCode(env);
return s_;
}
EmbeddedCode::EmbeddedCode() { segments_ = new EmbeddedCodeSegmentList(); }
EmbeddedCode::~EmbeddedCode() { delete_list(EmbeddedCodeSegmentList, segments_); }
void EmbeddedCode::Append(int atom) { current_segment_ += static_cast<char>(atom); }
void EmbeddedCode::Append(const char* str) { current_segment_ += str; }
void EmbeddedCode::Append(PacPrimitive* primitive) {
if ( ! current_segment_.empty() ) {
segments_->push_back(new EmbeddedCodeSegment(current_segment_));
current_segment_ = "";
}
segments_->push_back(new EmbeddedCodeSegment(primitive));
}
void EmbeddedCode::GenCode(Output* out, Env* env) {
if ( ! current_segment_.empty() ) {
segments_->push_back(new EmbeddedCodeSegment(current_segment_));
current_segment_ = "";
}
// TODO: return to the generated file after embedded code
// out->print("#line %d \"%s\"\n", line_num, filename.c_str());
// Allow use of RValue for undefined ID, in which case the
// ID's name is used as its RValue
env->set_allow_undefined_id(true);
foreach (i, EmbeddedCodeSegmentList, segments_) {
EmbeddedCodeSegment* segment = *i;
out->print("%s", segment->ToCode(env).c_str());
}
env->set_allow_undefined_id(false);
out->print("\n");
}

View file

@ -1,42 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_embedded_h
#define pac_embedded_h
#include "pac_common.h"
class EmbeddedCodeSegment {
public:
explicit EmbeddedCodeSegment(const string& s);
explicit EmbeddedCodeSegment(PacPrimitive* primitive);
~EmbeddedCodeSegment();
string ToCode(Env* env);
private:
string s_;
PacPrimitive* primitive_;
};
typedef vector<EmbeddedCodeSegment*> EmbeddedCodeSegmentList;
class EmbeddedCode : public Object {
public:
EmbeddedCode();
~EmbeddedCode();
// Append a character
void Append(int atom);
void Append(const char* str);
// Append a PAC primitive
void Append(PacPrimitive* primitive);
void GenCode(Output* out, Env* env);
private:
string current_segment_;
EmbeddedCodeSegmentList* segments_;
};
#endif // pac_embedded_h

View file

@ -1,60 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_enum.h"
#include "pac_exception.h"
#include "pac_expr.h"
#include "pac_exttype.h"
#include "pac_output.h"
#include "pac_typedecl.h"
Enum::Enum(ID* id, Expr* expr) : id_(id), expr_(expr) {}
Enum::~Enum() {
delete id_;
delete expr_;
}
void Enum::GenHeader(Output* out_h, int* pval) {
ASSERT(pval);
if ( expr_ ) {
if ( ! expr_->ConstFold(global_env(), pval) )
throw ExceptionNonConstExpr(expr_);
out_h->println("%s = %d,", id_->Name(), *pval);
}
else
out_h->println("%s,", id_->Name());
global_env()->AddConstID(id_, *pval);
}
EnumDecl::EnumDecl(ID* id, EnumList* enumlist) : Decl(id, ENUM), enumlist_(enumlist) {
ID* type_id = id->clone();
datatype_ = new ExternType(type_id, ExternType::NUMBER);
extern_typedecl_ = new TypeDecl(type_id, nullptr, datatype_);
}
EnumDecl::~EnumDecl() {
delete_list(EnumList, enumlist_);
delete extern_typedecl_;
}
void EnumDecl::Prepare() {
// Do nothing
}
void EnumDecl::GenForwardDeclaration(Output* out_h) {
out_h->println("// NOLINTNEXTLINE(performance-enum-size)");
out_h->println("enum %s {", id_->Name());
out_h->inc_indent();
int c = 0;
foreach (i, EnumList, enumlist_) {
(*i)->GenHeader(out_h, &c);
++c;
}
out_h->dec_indent();
out_h->println("};");
}
void EnumDecl::GenCode(Output* out_h, Output* /* out_cc */) {
// Do nothing
}

View file

@ -1,37 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_enum_h
#define pac_enum_h
#include "pac_decl.h"
class Enum {
public:
Enum(ID* id, Expr* expr = 0);
~Enum();
void GenHeader(Output* out_h, int* pval);
private:
ID* id_;
Expr* expr_;
};
class EnumDecl : public Decl {
public:
EnumDecl(ID* id, EnumList* enumlist);
~EnumDecl() override;
Type* DataType() const { return datatype_; }
void Prepare() override;
void GenForwardDeclaration(Output* out_h) override;
void GenCode(Output* out_h, Output* out_cc) override;
private:
EnumList* enumlist_;
Type* datatype_;
TypeDecl* extern_typedecl_;
};
#endif // pac_enum_h

View file

@ -1,63 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_exception.h"
#include "pac_expr.h"
#include "pac_id.h"
#include "pac_utils.h"
Exception::Exception(const Object* o, string msg) {
if ( o ) {
msg_ = o->Location();
msg_ += ": error : ";
}
msg_ += msg;
if ( FLAGS_pac_debug ) {
DEBUG_MSG("Exception: %s\n", msg_.c_str());
abort();
}
}
ExceptionIDNotFound::ExceptionIDNotFound(const ID* id) : Exception(id), id_(id) {
append(strfmt("`%s' undeclared", id_->Name()));
}
ExceptionIDRedefinition::ExceptionIDRedefinition(const ID* id) : Exception(id), id_(id) {
append(strfmt("`%s' redefined", id_->Name()));
}
ExceptionIDNotEvaluated::ExceptionIDNotEvaluated(const ID* id) : Exception(id), id_(id) {
append(strfmt("ID `%s' not evaluated before used", id->Name()));
}
ExceptionIDNotField::ExceptionIDNotField(const ID* id) : Exception(id), id_(id) {
append(strfmt("ID `%s' is not a field", id_->Name()));
}
ExceptionMemberNotFound::ExceptionMemberNotFound(const ID* type_id, const ID* member_id)
: Exception(member_id), type_id_(type_id), member_id_(member_id) {
append(strfmt("type %s does not have member `%s'", type_id_->Name(), member_id_->Name()));
}
ExceptionCyclicDependence::ExceptionCyclicDependence(const ID* id) : Exception(id), id_(id) {
append(strfmt("cyclic dependence through `%s'", id_->Name()));
}
ExceptionPaddingError::ExceptionPaddingError(const Object* o, string msg) : Exception(o) { append(msg.c_str()); }
ExceptionNonConstExpr::ExceptionNonConstExpr(const Expr* expr) : Exception(expr), expr(expr) {
append(strfmt("Expression `%s' is not constant", expr->orig()));
}
ExceptionInvalidCaseSizeExpr::ExceptionInvalidCaseSizeExpr(const Expr* expr) : Exception(expr), expr(expr) {
append(strfmt("Expression `%s' is greater than the 32-bit limit for use as a case index", expr->orig()));
}
ExceptionInvalidCaseLimitExpr::ExceptionInvalidCaseLimitExpr(const Expr* expr) : Exception(expr), expr(expr) {
append(
strfmt("Expression `%s' as a case index is outside the numeric limit of the type used "
"for the switch expression",
expr->orig()));
}

View file

@ -1,104 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_exception_h
#define pac_exception_h
#include <string>
using namespace std;
#include "pac_common.h"
class Exception {
public:
Exception(const Object* o, string msg = "");
const char* msg() const { return msg_.c_str(); }
void append(string s) { msg_ += s; }
private:
string msg_;
};
class ExceptionIDNotFound : public Exception {
public:
ExceptionIDNotFound(const ID* id);
const ID* id() const { return id_; }
private:
const ID* id_;
};
class ExceptionIDRedefinition : public Exception {
public:
ExceptionIDRedefinition(const ID* id);
const ID* id() const { return id_; }
private:
const ID* id_;
};
class ExceptionIDNotEvaluated : public Exception {
public:
ExceptionIDNotEvaluated(const ID* id);
const ID* id() const { return id_; }
private:
const ID* id_;
};
class ExceptionCyclicDependence : public Exception {
public:
ExceptionCyclicDependence(const ID* id);
const ID* id() const { return id_; }
private:
const ID* id_;
};
class ExceptionPaddingError : public Exception {
public:
ExceptionPaddingError(const Object* o, string msg);
};
class ExceptionIDNotField : public Exception {
public:
ExceptionIDNotField(const ID* id);
const ID* id() const { return id_; }
private:
const ID* id_;
};
class ExceptionMemberNotFound : public Exception {
public:
ExceptionMemberNotFound(const ID* type_id, const ID* member_id);
private:
const ID *type_id_, *member_id_;
};
class ExceptionNonConstExpr : public Exception {
public:
ExceptionNonConstExpr(const Expr* expr);
private:
const Expr* expr;
};
class ExceptionInvalidCaseSizeExpr : public Exception {
public:
ExceptionInvalidCaseSizeExpr(const Expr* expr);
private:
const Expr* expr;
};
class ExceptionInvalidCaseLimitExpr : public Exception {
public:
ExceptionInvalidCaseLimitExpr(const Expr* expr);
private:
const Expr* expr;
};
#endif /* pac_exception_h */

View file

@ -1,860 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_expr.h"
#include "pac_case.h"
#include "pac_cstr.h"
#include "pac_exception.h"
#include "pac_exttype.h"
#include "pac_id.h"
#include "pac_nullptr.h"
#include "pac_number.h"
#include "pac_output.h"
#include "pac_record.h"
#include "pac_regex.h"
#include "pac_strtype.h"
#include "pac_typedecl.h"
#include "pac_utils.h"
string OrigExprList(ExprList* list) {
bool first = true;
string str;
foreach (i, ExprList, list) {
Expr* expr = *i;
if ( first )
first = false;
else
str += ", ";
str += expr->orig();
}
return str;
}
string EvalExprList(ExprList* exprlist, Output* out, Env* env) {
string val_list("");
bool first = true;
foreach (i, ExprList, exprlist) {
if ( ! first )
val_list += ", ";
val_list += (*i)->EvalExpr(out, env);
first = false;
}
return val_list;
}
static const char* expr_fmt[] = {
#define EXPR_DEF(type, num_op, fmt) fmt,
#include "pac_expr.def"
#undef EXPR_DEF
};
void Expr::init() {
id_ = nullptr;
num_ = nullptr;
cstr_ = nullptr;
regex_ = nullptr;
num_operands_ = 0;
operand_[0] = nullptr;
operand_[1] = nullptr;
operand_[2] = nullptr;
args_ = nullptr;
cases_ = nullptr;
}
Expr::Expr(ID* arg_id) : DataDepElement(EXPR) {
init();
expr_type_ = EXPR_ID;
id_ = arg_id;
orig_ = strfmt("%s", id_->Name());
}
Expr::Expr(Number* arg_num) : DataDepElement(EXPR) {
init();
expr_type_ = EXPR_NUM;
num_ = arg_num;
orig_ = strfmt("((int) %s)", num_->Str());
}
Expr::Expr(Nullptr* arg_nullp) : DataDepElement(EXPR) {
init();
expr_type_ = EXPR_NULLPTR;
nullp_ = arg_nullp;
orig_ = strfmt("%s", nullp_->Str());
}
Expr::Expr(ConstString* cstr) : DataDepElement(EXPR) {
init();
expr_type_ = EXPR_CSTR;
cstr_ = cstr;
orig_ = cstr_->str();
}
Expr::Expr(RegEx* regex) : DataDepElement(EXPR) {
init();
expr_type_ = EXPR_REGEX;
regex_ = regex;
orig_ = strfmt("/%s/", regex_->str().c_str());
}
Expr::Expr(ExprType arg_type, Expr* op1) : DataDepElement(EXPR) {
init();
expr_type_ = arg_type;
num_operands_ = 1;
operand_[0] = op1;
orig_ = strfmt(expr_fmt[expr_type_], op1->orig());
}
Expr::Expr(ExprType arg_type, Expr* op1, Expr* op2) : DataDepElement(EXPR) {
init();
expr_type_ = arg_type;
num_operands_ = 2;
operand_[0] = op1;
operand_[1] = op2;
operand_[2] = nullptr;
orig_ = strfmt(expr_fmt[expr_type_], op1->orig(), op2->orig());
}
Expr::Expr(ExprType arg_type, Expr* op1, Expr* op2, Expr* op3) : DataDepElement(EXPR) {
init();
expr_type_ = arg_type;
num_operands_ = 3;
operand_[0] = op1;
operand_[1] = op2;
operand_[2] = op3;
orig_ = strfmt(expr_fmt[expr_type_], op1->orig(), op2->orig(), op3->orig());
}
Expr::Expr(ExprList* args) : DataDepElement(EXPR) {
init();
expr_type_ = EXPR_CALLARGS;
num_operands_ = -1;
args_ = args;
orig_ = OrigExprList(args_);
}
Expr::Expr(Expr* index, CaseExprList* cases) : DataDepElement(EXPR) {
init();
expr_type_ = EXPR_CASE;
num_operands_ = -1;
operand_[0] = index;
cases_ = cases;
orig_ = strfmt("case %s of { ", index->orig());
foreach (i, CaseExprList, cases_) {
CaseExpr* c = *i;
orig_ += strfmt("%s => %s; ", OrigExprList(c->index()).c_str(), c->value()->orig());
}
orig_ += "}";
}
Expr::~Expr() {
delete id_;
delete operand_[0];
delete operand_[1];
delete operand_[2];
delete_list(ExprList, args_);
delete_list(CaseExprList, cases_);
}
void Expr::AddCaseExpr(CaseExpr* case_expr) {
ASSERT(str_.empty());
ASSERT(expr_type_ == EXPR_CASE);
ASSERT(cases_);
cases_->push_back(case_expr);
}
void Expr::GenStrFromFormat(Env* env) {
// The format != "@custom@"
ASSERT(*expr_fmt[expr_type_] != '@');
switch ( num_operands_ ) {
case 1: str_ = strfmt(expr_fmt[expr_type_], operand_[0]->str()); break;
case 2: str_ = strfmt(expr_fmt[expr_type_], operand_[0]->str(), operand_[1]->str()); break;
case 3: str_ = strfmt(expr_fmt[expr_type_], operand_[0]->str(), operand_[1]->str(), operand_[2]->str()); break;
default:
DEBUG_MSG("num_operands_ = %d, orig = %s\n", num_operands_, orig());
ASSERT(0);
break;
}
}
namespace {
RecordField* GetRecordField(const ID* id, Env* env) {
Field* field = env->GetField(id);
ASSERT(field);
if ( field->tof() != RECORD_FIELD && field->tof() != PADDING_FIELD )
throw Exception(id, "not a record field");
RecordField* r = static_cast<RecordField*>(field);
ASSERT(r);
return r;
}
} // namespace
void Expr::GenCaseEval(Output* out_cc, Env* env) {
ASSERT(expr_type_ == EXPR_CASE);
ASSERT(operand_[0]);
ASSERT(cases_);
Type* val_type = DataType(env);
ID* val_var = env->AddTempID(val_type);
// DataType(env) can return a null pointer if an enum value is not
// defined.
if ( ! val_type )
throw Exception(this, "undefined case value");
out_cc->println("%s %s;", val_type->DataTypeStr().c_str(), env->LValue(val_var));
// force evaluation of IDs appearing in case stmt
operand_[0]->ForceIDEval(out_cc, env);
foreach (i, CaseExprList, cases_)
(*i)->value()->ForceIDEval(out_cc, env);
out_cc->println("// NOLINTBEGIN(bugprone-branch-clone)");
out_cc->println("switch ( %s ) {", operand_[0]->EvalExpr(out_cc, env));
Type* switch_type = operand_[0]->DataType(env);
out_cc->inc_indent();
CaseExpr* default_case = nullptr;
foreach (i, CaseExprList, cases_) {
CaseExpr* c = *i;
ExprList* index = c->index();
if ( ! index ) {
if ( default_case )
throw Exception(c, "duplicate default cases");
default_case = c;
}
else {
GenCaseStr(index, out_cc, env, switch_type);
out_cc->inc_indent();
out_cc->println("%s = %s;", env->LValue(val_var), c->value()->EvalExpr(out_cc, env));
out_cc->println("break;");
out_cc->dec_indent();
}
}
// Generate the default case after all other cases
GenCaseStr(nullptr, out_cc, env, switch_type);
out_cc->inc_indent();
if ( default_case ) {
out_cc->println("%s = %s;", env->LValue(val_var), default_case->value()->EvalExpr(out_cc, env));
}
else {
out_cc->println("throw binpac::ExceptionInvalidCaseIndex(\"%s\", (int64)%s);", Location(),
operand_[0]->EvalExpr(out_cc, env));
}
out_cc->println("break;");
out_cc->dec_indent();
out_cc->dec_indent();
out_cc->println("}");
out_cc->println("// NOLINTEND(bugprone-branch-clone)");
env->SetEvaluated(val_var);
str_ = env->RValue(val_var);
}
void Expr::GenEval(Output* out_cc, Env* env) {
switch ( expr_type_ ) {
case EXPR_NUM: str_ = num_->Str(); break;
case EXPR_NULLPTR: str_ = nullp_->Str(); break;
case EXPR_ID:
if ( ! env->Evaluated(id_) )
env->Evaluate(out_cc, id_);
str_ = env->RValue(id_);
break;
case EXPR_MEMBER: {
/*
For member expressions such X.Y, evaluating
X only is sufficient. (Actually trying to
evaluate Y will lead to error because Y is
not defined in the current environment.)
*/
operand_[0]->GenEval(out_cc, env);
Type* ty0 = operand_[0]->DataType(env);
if ( ty0 ) {
str_ = strfmt("%s%s", operand_[0]->EvalExpr(out_cc, env), ty0->EvalMember(operand_[1]->id()).c_str());
}
else {
string tmp = strfmt("->%s()", operand_[1]->id()->Name());
str_ = strfmt("%s%s", operand_[0]->EvalExpr(out_cc, env), tmp.c_str());
}
} break;
case EXPR_SUBSCRIPT: {
operand_[0]->GenEval(out_cc, env);
operand_[1]->GenEval(out_cc, env);
string v0 = operand_[0]->EvalExpr(out_cc, env);
string v1 = operand_[1]->EvalExpr(out_cc, env);
Type* ty0 = operand_[0]->DataType(env);
if ( ty0 )
str_ = ty0->EvalElement(v0, v1);
else
str_ = strfmt("%s[%s]", v0.c_str(), v1.c_str());
} break;
case EXPR_SIZEOF: {
const ID* id = operand_[0]->id();
RecordField* rf;
Type* ty;
try {
if ( (rf = GetRecordField(id, env)) != nullptr ) {
str_ = strfmt("%s", rf->FieldSize(out_cc, env));
}
} catch ( ExceptionIDNotFound& e ) {
if ( (ty = TypeDecl::LookUpType(id)) != nullptr ) {
int ty_size = ty->StaticSize(global_env());
if ( ty_size >= 0 )
str_ = strfmt("%d", ty_size);
else
throw Exception(id, "unknown size");
}
else
throw Exception(id, "not a record field or type");
}
} break;
case EXPR_OFFSETOF: {
const ID* id = operand_[0]->id();
RecordField* rf = GetRecordField(id, env);
str_ = strfmt("%s", rf->FieldOffset(out_cc, env));
} break;
case EXPR_CALLARGS: str_ = EvalExprList(args_, out_cc, env); break;
case EXPR_CASE: GenCaseEval(out_cc, env); break;
default:
// Evaluate every operand by default
for ( int i = 0; i < 3; ++i )
if ( operand_[i] )
operand_[i]->GenEval(out_cc, env);
GenStrFromFormat(env);
break;
}
}
void Expr::ForceIDEval(Output* out_cc, Env* env) {
switch ( expr_type_ ) {
case EXPR_NUM:
case EXPR_SIZEOF:
case EXPR_OFFSETOF: break;
case EXPR_ID:
if ( ! env->Evaluated(id_) )
env->Evaluate(out_cc, id_);
break;
case EXPR_MEMBER: operand_[0]->ForceIDEval(out_cc, env); break;
case EXPR_CALLARGS: {
foreach (i, ExprList, args_)
(*i)->ForceIDEval(out_cc, env);
} break;
case EXPR_CASE: {
operand_[0]->ForceIDEval(out_cc, env);
foreach (i, CaseExprList, cases_)
(*i)->value()->ForceIDEval(out_cc, env);
} break;
default:
// Evaluate every operand by default
for ( int i = 0; i < 3; ++i )
if ( operand_[i] )
operand_[i]->ForceIDEval(out_cc, env);
break;
}
}
const char* Expr::EvalExpr(Output* out_cc, Env* env) {
GenEval(out_cc, env);
return str();
}
Type* Expr::DataType(Env* env) const {
Type* data_type;
switch ( expr_type_ ) {
case EXPR_ID: data_type = env->GetDataType(id_); break;
case EXPR_MEMBER: {
// Get type of the parent
Type* parent_type = operand_[0]->DataType(env);
if ( ! parent_type )
return nullptr;
data_type = parent_type->MemberDataType(operand_[1]->id());
} break;
case EXPR_SUBSCRIPT: {
// Get type of the parent
Type* parent_type = operand_[0]->DataType(env);
data_type = parent_type->ElementDataType();
} break;
case EXPR_PAREN: data_type = operand_[0]->DataType(env); break;
case EXPR_COND: {
Type* type1 = operand_[1]->DataType(env);
Type* type2 = operand_[2]->DataType(env);
if ( ! Type::CompatibleTypes(type1, type2) ) {
throw Exception(this, strfmt("type mismatch: %s vs %s", type1->DataTypeStr().c_str(),
type2->DataTypeStr().c_str()));
}
data_type = type1;
} break;
case EXPR_CALL: data_type = operand_[0]->DataType(env); break;
case EXPR_CASE: {
if ( cases_ && ! cases_->empty() ) {
Type* type1 = cases_->front()->value()->DataType(env);
Type* numeric_with_largest_width = nullptr;
foreach (i, CaseExprList, cases_) {
Type* type2 = (*i)->value()->DataType(env);
if ( ! Type::CompatibleTypes(type1, type2) ) {
throw Exception(this, strfmt("type mismatch: %s vs %s", type1->DataTypeStr().c_str(),
type2->DataTypeStr().c_str()));
}
if ( type1 == extern_type_nullptr )
type1 = type2;
if ( type2 && type2->IsNumericType() ) {
if ( numeric_with_largest_width ) {
int largest;
int contender;
// External C++ types like "int", "bool", "enum" use "int"
// storage internally.
if ( numeric_with_largest_width->tot() == Type::EXTERN )
largest = sizeof(int);
else
largest = numeric_with_largest_width->StaticSize(env);
if ( type2->tot() == Type::EXTERN )
contender = sizeof(int);
else
contender = type2->StaticSize(env);
if ( contender > largest )
numeric_with_largest_width = type2;
}
else
numeric_with_largest_width = type2;
}
}
data_type = numeric_with_largest_width ? numeric_with_largest_width : type1;
}
else
data_type = nullptr;
} break;
case EXPR_NUM:
case EXPR_SIZEOF:
case EXPR_OFFSETOF:
case EXPR_NEG:
case EXPR_PLUS:
case EXPR_MINUS:
case EXPR_TIMES:
case EXPR_DIV:
case EXPR_MOD:
case EXPR_BITNOT:
case EXPR_BITAND:
case EXPR_BITOR:
case EXPR_BITXOR:
case EXPR_LSHIFT:
case EXPR_RSHIFT:
case EXPR_EQUAL:
case EXPR_GE:
case EXPR_LE:
case EXPR_GT:
case EXPR_LT:
case EXPR_NOT:
case EXPR_AND:
case EXPR_OR: data_type = extern_type_int; break;
default: data_type = nullptr; break;
}
return data_type;
}
string Expr::DataTypeStr(Env* env) const {
Type* type = DataType(env);
if ( ! type ) {
throw Exception(this, strfmt("cannot find data type for expression `%s'", orig()));
}
return type->DataTypeStr();
}
string Expr::SetFunc(Output* out, Env* env) {
switch ( expr_type_ ) {
case EXPR_ID: return set_function(id_);
case EXPR_MEMBER: {
// Evaluate the parent
string parent_val(operand_[0]->EvalExpr(out, env));
return parent_val + "->" + set_function(operand_[1]->id());
} break;
default:
throw Exception(this, strfmt("cannot generate set function "
"for expression `%s'",
orig()));
break;
}
}
bool Expr::ConstFold(Env* env, int* pn) const {
switch ( expr_type_ ) {
case EXPR_NUM: *pn = num_->Num(); return true;
case EXPR_ID: return env->GetConstant(id_, pn);
default:
// ### FIXME: folding consts
return false;
}
}
// TODO: build a generic data dependency extraction process
namespace {
// Maximum of two minimal header sizes
int mhs_max(int h1, int h2) {
if ( h1 < 0 || h2 < 0 )
return -1;
else {
// return max(h1, h2);
return h1 > h2 ? h1 : h2;
}
}
// MHS required to evaluate the field
int mhs_letfield(Env* env, LetField* field) { return field->expr()->MinimalHeaderSize(env); }
int mhs_recordfield(Env* env, RecordField* field) {
int offset = field->static_offset();
if ( offset < 0 ) // offset cannot be statically determined
return -1;
int size = field->StaticSize(env, offset);
if ( size < 0 ) // size cannot be statically determined
return -1;
return offset + size;
}
int mhs_casefield(Env* env, CaseField* field) {
// TODO: deal with the index
int size = field->StaticSize(env);
if ( size < 0 ) // size cannot be statically determined
return -1;
return size;
}
int mhs_field(Env* env, Field* field) {
int mhs = -1;
switch ( field->tof() ) {
case LET_FIELD: {
LetField* f = static_cast<LetField*>(field);
ASSERT(f);
mhs = mhs_letfield(env, f);
} break;
case CONTEXT_FIELD:
case FLOW_FIELD: ASSERT(0); break;
case PARAM_FIELD: mhs = 0; break;
case RECORD_FIELD:
case PADDING_FIELD: {
RecordField* f = static_cast<RecordField*>(field);
ASSERT(f);
mhs = mhs_recordfield(env, f);
} break;
case CASE_FIELD: {
CaseField* f = static_cast<CaseField*>(field);
ASSERT(f);
mhs = mhs_casefield(env, f);
} break;
case PARSE_VAR_FIELD:
case PRIV_VAR_FIELD:
case PUB_VAR_FIELD:
case TEMP_VAR_FIELD: mhs = 0; break;
case WITHINPUT_FIELD: {
// ### TODO: fix this
mhs = -1;
} break;
}
return mhs;
}
int mhs_id(Env* env, const ID* id) {
int mhs = -1;
switch ( env->GetIDType(id) ) {
case CONST:
case GLOBAL_VAR:
case TEMP_VAR:
case STATE_VAR:
case FUNC_ID:
case FUNC_PARAM: mhs = 0; break;
case MEMBER_VAR:
case PRIV_MEMBER_VAR: {
Field* field = env->GetField(id);
if ( ! field )
throw ExceptionIDNotField(id);
mhs = mhs_field(env, field);
} break;
case UNION_VAR:
// TODO: deal with UNION_VAR
mhs = -1;
break;
case MACRO: {
Expr* e = env->GetMacro(id);
mhs = e->MinimalHeaderSize(env);
} break;
}
return mhs;
}
} // namespace
int Expr::MinimalHeaderSize(Env* env) {
int mhs;
switch ( expr_type_ ) {
case EXPR_NUM:
// Zero byte is required
mhs = 0;
break;
case EXPR_ID: mhs = mhs_id(env, id_); break;
case EXPR_MEMBER:
// TODO: this is not a tight bound because
// one actually does not have to parse the
// whole record to compute one particular
// field.
mhs = operand_[0]->MinimalHeaderSize(env);
break;
case EXPR_SUBSCRIPT: {
int index;
Type* array_type = operand_[0]->DataType(env);
Type* elem_type = array_type->ElementDataType();
int elem_size = elem_type->StaticSize(env);
if ( elem_size >= 0 && operand_[1]->ConstFold(env, &index) ) {
mhs = elem_size * index;
}
else {
mhs = -1;
}
} break;
case EXPR_SIZEOF: {
const ID* id = operand_[0]->id();
ASSERT(id);
RecordField* rf;
Type* ty;
if ( (rf = GetRecordField(id, env)) != nullptr ) {
if ( rf->StaticSize(env, -1) >= 0 )
mhs = 0;
else
mhs = mhs_recordfield(env, rf);
}
else if ( (ty = TypeDecl::LookUpType(id)) != nullptr ) {
mhs = 0;
}
else
throw Exception(id, "not a record field or type");
} break;
case EXPR_OFFSETOF: {
const ID* id = operand_[0]->id();
ASSERT(id);
RecordField* field = GetRecordField(id, env);
mhs = field->static_offset();
if ( mhs < 0 ) {
mhs = 0;
// Take the MHS of the preceding (non-let) field
RecordField* prev_field = field->prev();
ASSERT(prev_field);
mhs = mhs_recordfield(env, prev_field);
}
} break;
case EXPR_CALLARGS: {
mhs = 0;
if ( args_ )
for ( unsigned int i = 0; i < args_->size(); ++i )
mhs = mhs_max(mhs, (*args_)[i]->MinimalHeaderSize(env));
} break;
case EXPR_CASE: {
mhs = operand_[0]->MinimalHeaderSize(env);
for ( unsigned int i = 0; i < cases_->size(); ++i ) {
CaseExpr* ce = (*cases_)[i];
if ( ce->index() )
for ( unsigned int j = 0; j < ce->index()->size(); ++j )
mhs = mhs_max(mhs, (*ce->index())[j]->MinimalHeaderSize(env));
mhs = mhs_max(mhs, ce->value()->MinimalHeaderSize(env));
}
} break;
default:
// Evaluate every operand by default
mhs = 0;
for ( int i = 0; i < 3; ++i )
if ( operand_[i] )
mhs = mhs_max(mhs, operand_[i]->MinimalHeaderSize(env));
break;
}
return mhs;
}
bool Expr::HasReference(const ID* id) const {
switch ( expr_type_ ) {
case EXPR_ID: return *id == *id_;
case EXPR_MEMBER: return operand_[0]->HasReference(id);
case EXPR_CALLARGS: {
foreach (i, ExprList, args_)
if ( (*i)->HasReference(id) )
return true;
}
return false;
case EXPR_CASE: {
foreach (i, CaseExprList, cases_)
if ( (*i)->HasReference(id) )
return true;
}
return false;
default:
// Evaluate every operand by default
for ( int i = 0; i < 3; ++i ) {
if ( operand_[i] && operand_[i]->HasReference(id) ) {
return true;
}
}
return false;
}
}
bool Expr::DoTraverse(DataDepVisitor* visitor) {
switch ( expr_type_ ) {
case EXPR_ID: break;
case EXPR_MEMBER:
/*
For member expressions such X.Y, evaluating
X only is sufficient. (Actually trying to
evaluate Y will lead to error because Y is
not defined in the current environment.)
*/
if ( ! operand_[0]->Traverse(visitor) )
return false;
break;
case EXPR_CALLARGS: {
foreach (i, ExprList, args_)
if ( ! (*i)->Traverse(visitor) )
return false;
} break;
case EXPR_CASE: {
foreach (i, CaseExprList, cases_)
if ( ! (*i)->Traverse(visitor) )
return false;
} break;
default:
// Evaluate every operand by default
for ( int i = 0; i < 3; ++i ) {
if ( operand_[i] && ! operand_[i]->Traverse(visitor) ) {
return false;
}
}
break;
}
return true;
}
bool Expr::RequiresAnalyzerContext() const {
switch ( expr_type_ ) {
case EXPR_ID: return *id_ == *analyzer_context_id;
case EXPR_MEMBER:
/*
For member expressions such X.Y, evaluating
X only is sufficient. (Actually trying to
evaluate Y will lead to error because Y is
not defined in the current environment.)
*/
return operand_[0]->RequiresAnalyzerContext();
case EXPR_CALLARGS: {
foreach (i, ExprList, args_)
if ( (*i)->RequiresAnalyzerContext() )
return true;
}
return false;
case EXPR_CASE: {
foreach (i, CaseExprList, cases_)
if ( (*i)->RequiresAnalyzerContext() )
return true;
}
return false;
default:
// Evaluate every operand by default
for ( int i = 0; i < 3; ++i )
if ( operand_[i] && operand_[i]->RequiresAnalyzerContext() ) {
DEBUG_MSG("'%s' requires analyzer context\n", operand_[i]->orig());
return true;
}
return false;
}
}
CaseExpr::CaseExpr(ExprList* index, Expr* value)
: DataDepElement(DataDepElement::CASEEXPR), index_(index), value_(value) {}
CaseExpr::~CaseExpr() {
delete_list(ExprList, index_);
delete value_;
}
bool CaseExpr::DoTraverse(DataDepVisitor* visitor) {
foreach (i, ExprList, index_)
if ( ! (*i)->Traverse(visitor) )
return false;
return value_->Traverse(visitor);
}
bool CaseExpr::HasReference(const ID* id) const { return value_->HasReference(id); }
bool CaseExpr::RequiresAnalyzerContext() const {
// index_ should evaluate to constants
return value_->RequiresAnalyzerContext();
}

View file

@ -1,35 +0,0 @@
EXPR_DEF(EXPR_ID, 0, "%s")
EXPR_DEF(EXPR_NUM, 0, "%s")
EXPR_DEF(EXPR_NULLPTR, 0, "%s")
EXPR_DEF(EXPR_CSTR, 0, "%s")
EXPR_DEF(EXPR_REGEX, 0, "REGEX(%s)")
EXPR_DEF(EXPR_SUBSCRIPT, 2, "@element@(%s[%s])")
EXPR_DEF(EXPR_MEMBER, 2, "@%s->%s@")
EXPR_DEF(EXPR_PAREN, 1, " ( %s ) ")
EXPR_DEF(EXPR_CALL, 1, "%s(%s)")
EXPR_DEF(EXPR_CALLARGS, -1, "@custom@")
EXPR_DEF(EXPR_SIZEOF, 1, "@sizeof(%s)@")
EXPR_DEF(EXPR_OFFSETOF, 1, "@offsetof(%s)@")
EXPR_DEF(EXPR_NEG, 1, "-%s")
EXPR_DEF(EXPR_PLUS, 2, "%s + %s")
EXPR_DEF(EXPR_MINUS, 2, "%s - %s")
EXPR_DEF(EXPR_TIMES, 2, "%s * %s")
EXPR_DEF(EXPR_DIV, 2, "%s / %s")
EXPR_DEF(EXPR_MOD, 2, "%s %% %s")
EXPR_DEF(EXPR_BITNOT, 1, "~%s")
EXPR_DEF(EXPR_BITAND, 2, "%s & %s")
EXPR_DEF(EXPR_BITOR, 2, "%s | %s")
EXPR_DEF(EXPR_BITXOR, 2, "%s ^ %s")
EXPR_DEF(EXPR_LSHIFT, 2, "%s << %s")
EXPR_DEF(EXPR_RSHIFT, 2, "%s >> %s")
EXPR_DEF(EXPR_EQUAL, 2, "%s == %s")
EXPR_DEF(EXPR_NEQ, 2, "%s != %s")
EXPR_DEF(EXPR_GE, 2, "%s >= %s")
EXPR_DEF(EXPR_LE, 2, "%s <= %s")
EXPR_DEF(EXPR_GT, 2, "%s > %s")
EXPR_DEF(EXPR_LT, 2, "%s < %s")
EXPR_DEF(EXPR_NOT, 1, "! %s")
EXPR_DEF(EXPR_AND, 2, "%s && %s")
EXPR_DEF(EXPR_OR, 2, "%s || %s")
EXPR_DEF(EXPR_COND, 3, "%s ? %s : %s")
EXPR_DEF(EXPR_CASE, -1, "@custom@")

View file

@ -1,143 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_expr_h
#define pac_expr_h
#include <cstdint>
#include "pac_common.h"
#include "pac_datadep.h"
class CaseExpr;
class Expr : public Object, public DataDepElement {
public:
enum ExprType : uint8_t {
#define EXPR_DEF(type, x, y) type,
#include "pac_expr.def"
#undef EXPR_DEF
};
void init();
Expr(ID* id);
Expr(Number* num);
Expr(Nullptr* nullp);
Expr(ConstString* s);
Expr(RegEx* regex);
Expr(ExprList* args); // for EXPR_CALLARGS
Expr(Expr* index, CaseExprList* cases);
Expr(ExprType type, Expr* op1);
Expr(ExprType type, Expr* op1, Expr* op2);
Expr(ExprType type, Expr* op1, Expr* op2, Expr* op3);
~Expr() override;
const char* orig() const { return orig_.c_str(); }
const ID* id() const { return id_; }
const char* str() const { return str_.c_str(); }
ExprType expr_type() const { return expr_type_; }
void AddCaseExpr(CaseExpr* case_expr);
// Returns the data "type" of the expression. Here we only
// do a serious job for the EXPR_MEMBER and EXPR_SUBSCRIPT
// operators. For arithmetic operations, we fall back
// to "int".
Type* DataType(Env* env) const;
string DataTypeStr(Env* env) const;
// Note: EvalExpr() may generate C++ statements in order to evaluate
// variables in the expression, so the following is wrong:
//
// out->print("int x = ");
// out->println("%s", expr->EvalExpr(out, env));
//
// While putting them together is right:
//
// out->println("int x = %s", expr->EvalExpr(out, env));
//
const char* EvalExpr(Output* out, Env* env);
// force evaluation of IDs contained in this expression;
// necessary with case expr and conditional let fields (&if)
// for correct parsing of fields
void ForceIDEval(Output* out_cc, Env* env);
// Returns the set_* function of the expression.
// The expression must be of form ID or x.ID.
string SetFunc(Output* out, Env* env);
// Returns true if the expression folds to an integer
// constant with env, and puts the constant in *pn.
//
bool ConstFold(Env* env, int* pn) const;
// Whether id is referenced in the expression
bool HasReference(const ID* id) const;
// Suppose the data for type might be incomplete, what is
// the minimal number of bytes from data head required to
// compute the expression? For example, how many bytes of frame
// header do we need to determine the length of the frame?
//
// The parameter <env> points to the Env of a type.
//
// Returns -1 if the number is not a constant.
//
int MinimalHeaderSize(Env* env);
// Whether evaluation of the expression requires the analyzer context
bool RequiresAnalyzerContext() const;
protected:
bool DoTraverse(DataDepVisitor* visitor) override;
private:
ExprType expr_type_;
int num_operands_ = 0;
Expr* operand_[3] = {nullptr};
ID* id_ = nullptr; // EXPR_ID
Number* num_ = nullptr; // EXPR_NUM
ConstString* cstr_ = nullptr; // EXPR_CSTR
RegEx* regex_ = nullptr; // EXPR_REGEX
ExprList* args_ = nullptr; // EXPR_CALLARGS
CaseExprList* cases_ = nullptr; // EXPR_CASE
Nullptr* nullp_ = nullptr; // EXPR_NULLPTR
string str_; // value string
string orig_; // original string for debugging info
void GenStrFromFormat(Env* env);
void GenEval(Output* out, Env* env);
void GenCaseEval(Output* out_cc, Env* env);
};
string OrigExprList(ExprList* exprlist);
string EvalExprList(ExprList* exprlist, Output* out, Env* env);
// An entry of the case expression, consisting of one or more constant
// expressions for the case index and a value expression.
class CaseExpr : public Object, public DataDepElement {
public:
CaseExpr(ExprList* index, Expr* value);
~CaseExpr() override;
ExprList* index() const { return index_; }
Expr* value() const { return value_; }
bool HasReference(const ID* id) const;
bool RequiresAnalyzerContext() const;
protected:
bool DoTraverse(DataDepVisitor* visitor) override;
private:
ExprList* index_;
Expr* value_;
};
#endif // pac_expr_h

View file

@ -1,15 +0,0 @@
EXTERNTYPE(bool, bool, BOOLEAN)
EXTERNTYPE(int, int, NUMBER)
EXTERNTYPE(double, double, NUMBER)
EXTERNTYPE(string, string, PLAIN)
EXTERNTYPE(void, void, PLAIN)
EXTERNTYPE(voidptr, void, POINTER)
EXTERNTYPE(nullptr, nullptr, PLAIN)
EXTERNTYPE(bytearray, bytearray, PLAIN)
EXTERNTYPE(const_charptr, const_charptr, PLAIN)
EXTERNTYPE(const_byteptr, const_byteptr, PLAIN)
// EXTERNTYPE(const_byteseg, const_byteseg, PLAIN)
EXTERNTYPE(const_bytestring, const_bytestring, PLAIN)
// EXTERNTYPE(bytestring, bytestring, PLAIN)
EXTERNTYPE(re_matcher, re_matcher, PLAIN)
EXTERNTYPE(flowbuffer, FlowBuffer, POINTER)

View file

@ -1,66 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_exttype.h"
#include "pac_decl.h"
#include "pac_id.h"
#include "pac_output.h"
bool ExternType::DefineValueVar() const { return true; }
string ExternType::DataTypeStr() const {
switch ( ext_type_ ) {
case PLAIN:
case NUMBER:
case BOOLEAN: return id_->Name();
case POINTER: return string(id_->Name()) + " *";
default: ASSERT(0); return "";
}
}
int ExternType::StaticSize(Env* env) const {
ASSERT(0);
return -1;
}
bool ExternType::ByteOrderSensitive() const { return false; }
string ExternType::EvalMember(const ID* member_id) const {
return strfmt("%s%s", ext_type_ == POINTER ? "->" : ".", member_id->Name());
}
void ExternType::GenInitCode(Output* out_cc, Env* env) {
if ( IsNumericType() )
out_cc->println("%s = 0;", env->LValue(value_var()));
else if ( IsPointerType() )
out_cc->println("%s = nullptr;", env->LValue(value_var()));
else if ( IsBooleanType() )
out_cc->println("%s = false;", env->LValue(value_var()));
Type::GenInitCode(out_cc, env);
}
void ExternType::DoGenParseCode(Output* out, Env* env, const DataPtr& data, int flags) { ASSERT(0); }
void ExternType::GenDynamicSize(Output* out, Env* env, const DataPtr& data) { ASSERT(0); }
Type* ExternType::DoClone() const { return new ExternType(id_->clone(), ext_type_); }
// Definitions of pre-defined external types
#define EXTERNTYPE(name, ctype, exttype) ExternType* extern_type_##name = 0;
#include "pac_externtype.def"
#undef EXTERNTYPE
void ExternType::static_init() {
ID* id;
// TypeDecl *decl;
// decl = new TypeDecl(id, 0, extern_type_##name);
#define EXTERNTYPE(name, ctype, exttype) \
id = new ID(#ctype); \
extern_type_##name = new ExternType(id, ExternType::exttype); \
Type::AddPredefinedType(#name, extern_type_##name);
#include "pac_externtype.def"
#undef EXTERNTYPE
}

View file

@ -1,48 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_exttype_h
#define pac_exttype_h
#include "pac_type.h"
// ExternType represent external C++ types that are not defined in
// PAC specification (therefore they cannot appear in data layout
// specification, e.g., in a record field). The type name is copied
// literally to the compiled code.
class ExternType : public Type {
public:
enum EXTType { PLAIN, NUMBER, POINTER, BOOLEAN };
ExternType(const ID* id, EXTType ext_type) : Type(EXTERN), id_(id), ext_type_(ext_type) {}
bool DefineValueVar() const override;
string DataTypeStr() const override;
int StaticSize(Env* env) const override;
bool ByteOrderSensitive() const override;
string EvalMember(const ID* member_id) const override;
bool IsNumericType() const override { return ext_type_ == NUMBER; }
bool IsPointerType() const override { return ext_type_ == POINTER; }
bool IsBooleanType() const override { return ext_type_ == BOOLEAN; }
void GenInitCode(Output* out_cc, Env* env) override;
protected:
void DoGenParseCode(Output* out, Env* env, const DataPtr& data, int flags) override;
void GenDynamicSize(Output* out, Env* env, const DataPtr& data) override;
Type* DoClone() const override;
private:
const ID* id_;
EXTType ext_type_;
public:
static void static_init();
};
#define EXTERNTYPE(name, ctype, exttype) extern ExternType* extern_type_##name;
#include "pac_externtype.def"
#undef EXTERNTYPE
#endif // pac_exttype_h

View file

@ -1,125 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_field.h"
#include "pac_attr.h"
#include "pac_common.h"
#include "pac_exception.h"
#include "pac_id.h"
#include "pac_type.h"
Field::Field(FieldType tof, int flags, ID* id, Type* type)
: DataDepElement(DataDepElement::FIELD), tof_(tof), flags_(flags), id_(id), type_(type) {
decl_id_ = current_decl_id;
field_id_str_ = strfmt("%s:%s", decl_id()->Name(), id_->Name());
attrs_ = nullptr;
}
Field::~Field() {
delete id_;
delete type_;
delete_list(AttrList, attrs_);
}
void Field::AddAttr(AttrList* attrs) {
bool delete_attrs = false;
if ( ! attrs_ ) {
attrs_ = attrs;
}
else {
attrs_->insert(attrs_->end(), attrs->begin(), attrs->end());
delete_attrs = true;
}
foreach (i, AttrList, attrs)
ProcessAttr(*i);
if ( delete_attrs )
delete attrs;
}
void Field::ProcessAttr(Attr* a) {
switch ( a->type() ) {
case ATTR_IF:
if ( tof() != LET_FIELD && tof() != WITHINPUT_FIELD ) {
throw Exception(a,
"&if can only be applied to a "
"let field");
}
break;
default: break;
}
if ( type_ )
type_->ProcessAttr(a);
}
bool Field::anonymous_field() const { return type_ && type_->anonymous_value_var(); }
int Field::ValueVarType() const {
if ( flags_ & CLASS_MEMBER )
return (flags_ & PUBLIC_READABLE) ? MEMBER_VAR : PRIV_MEMBER_VAR;
else
return TEMP_VAR;
}
void Field::Prepare(Env* env) {
if ( type_ ) {
if ( anonymous_field() )
flags_ &= ~(CLASS_MEMBER | PUBLIC_READABLE);
if ( ! type_->persistent() )
flags_ &= (~PUBLIC_READABLE);
type_->set_value_var(id(), ValueVarType());
type_->Prepare(env, flags_ & TYPE_TO_BE_PARSED ? Type::TO_BE_PARSED : 0);
env->SetField(id(), this);
}
}
void Field::GenPubDecls(Output* out_h, Env* env) {
if ( type_ && (flags_ & PUBLIC_READABLE) && (flags_ & CLASS_MEMBER) )
type_->GenPubDecls(out_h, env);
}
void Field::GenPrivDecls(Output* out_h, Env* env) {
// Generate private declaration only if it is a class member
if ( type_ && (flags_ & CLASS_MEMBER) )
type_->GenPrivDecls(out_h, env);
}
void Field::GenTempDecls(Output* out_h, Env* env) {
// Generate temp field
if ( type_ && ! (flags_ & CLASS_MEMBER) )
type_->GenPrivDecls(out_h, env);
}
void Field::GenInitCode(Output* out_cc, Env* env) {
if ( type_ && ! anonymous_field() )
type_->GenInitCode(out_cc, env);
}
void Field::GenCleanUpCode(Output* out_cc, Env* env) {
if ( type_ && ! anonymous_field() )
type_->GenCleanUpCode(out_cc, env);
}
bool Field::DoTraverse(DataDepVisitor* visitor) {
// Check parameterized type
if ( type_ && ! type_->Traverse(visitor) )
return false;
foreach (i, AttrList, attrs_)
if ( ! (*i)->Traverse(visitor) )
return false;
return true;
}
bool Field::RequiresAnalyzerContext() const {
// Check parameterized type
if ( type_ && type_->RequiresAnalyzerContext() )
return true;
foreach (i, AttrList, attrs_)
if ( (*i)->RequiresAnalyzerContext() )
return true;
return false;
}

View file

@ -1,85 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_field_h
#define pac_field_h
#include "pac_common.h"
#include "pac_datadep.h"
// A "field" is a member of class.
enum FieldType {
CASE_FIELD,
CONTEXT_FIELD,
FLOW_FIELD,
LET_FIELD,
PADDING_FIELD,
PARAM_FIELD,
RECORD_FIELD,
PARSE_VAR_FIELD,
PRIV_VAR_FIELD,
PUB_VAR_FIELD,
TEMP_VAR_FIELD,
WITHINPUT_FIELD,
};
class Field : public Object, public DataDepElement {
public:
Field(FieldType tof, int flags, ID* id, Type* type);
// Field flags
// Whether the field will be evaluated by calling the Parse()
// function of the type
static const int TYPE_TO_BE_PARSED = 1;
static const int TYPE_NOT_TO_BE_PARSED = 0;
// Whether the field is a member of the class or a temp
// variable
static const int CLASS_MEMBER = 2;
static const int NOT_CLASS_MEMBER = 0;
// Whether the field is public readable
static const int PUBLIC_READABLE = 4;
static const int NOT_PUBLIC_READABLE = 0;
~Field() override;
FieldType tof() const { return tof_; }
const ID* id() const { return id_; }
Type* type() const { return type_; }
const ID* decl_id() const { return decl_id_; }
bool anonymous_field() const;
void AddAttr(AttrList* attrs);
// The field interface
virtual void ProcessAttr(Attr* attr);
virtual void Prepare(Env* env);
virtual void GenPubDecls(Output* out, Env* env);
virtual void GenPrivDecls(Output* out, Env* env);
virtual void GenTempDecls(Output* out, Env* env);
virtual void GenInitCode(Output* out, Env* env);
virtual void GenCleanUpCode(Output* out, Env* env);
virtual bool RequiresAnalyzerContext() const;
protected:
int ValueVarType() const;
bool ToBeParsed() const;
bool DoTraverse(DataDepVisitor* visitor) override;
protected:
FieldType tof_;
int flags_;
ID* id_;
Type* type_;
const ID* decl_id_;
string field_id_str_;
AttrList* attrs_;
};
#endif // pac_field_h

View file

@ -1,262 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "pac_flow.h"
#include "pac_analyzer.h"
#include "pac_conn.h"
#include "pac_context.h"
#include "pac_dataptr.h"
#include "pac_dataunit.h"
#include "pac_embedded.h"
#include "pac_exception.h"
#include "pac_expr.h"
#include "pac_exttype.h"
#include "pac_output.h"
#include "pac_param.h"
#include "pac_paramtype.h"
#include "pac_type.h"
#include "pac_varfield.h"
FlowDecl::FlowDecl(ID* id, ParamList* params, AnalyzerElementList* elemlist) : AnalyzerDecl(id, FLOW, params) {
dataunit_ = nullptr;
conn_decl_ = nullptr;
flow_buffer_var_field_ = nullptr;
AddElements(elemlist);
}
FlowDecl::~FlowDecl() {
delete flow_buffer_var_field_;
delete dataunit_;
}
ParameterizedType* FlowDecl::flow_buffer_type_ = nullptr;
ParameterizedType* FlowDecl::flow_buffer_type() {
if ( ! flow_buffer_type_ ) {
flow_buffer_type_ = new ParameterizedType(new ID(kFlowBufferClass), nullptr);
}
return flow_buffer_type_;
}
void FlowDecl::AddBaseClass(vector<string>* base_classes) const { base_classes->push_back("binpac::FlowAnalyzer"); }
void FlowDecl::ProcessFlowElement(AnalyzerFlow* flow_elem) {
throw Exception(flow_elem, "flow should be defined in only a connection declaration");
}
void FlowDecl::ProcessDataUnitElement(AnalyzerDataUnit* dataunit_elem) {
if ( dataunit_ ) {
throw Exception(dataunit_elem, "dataunit already defined");
}
dataunit_ = dataunit_elem;
if ( dataunit_->type() == AnalyzerDataUnit::FLOWUNIT ) {
dataunit_->data_type()->MarkIncrementalInput();
flow_buffer_var_field_ = new PubVarField(flow_buffer_id->clone(), FlowDecl::flow_buffer_type()->Clone());
type_->AddField(flow_buffer_var_field_);
ASSERT(AnalyzerContextDecl::current_analyzer_context());
AnalyzerContextDecl::current_analyzer_context()->AddFlowBuffer();
// Add an argument to the context initiation
dataunit_->context_type()->AddParamArg(new Expr(flow_buffer_var_field_->id()->clone()));
}
}
void FlowDecl::Prepare() {
// Add the connection parameter
if ( ! conn_decl_ ) {
throw Exception(this, "no connection is not declared for the flow");
}
if ( ! params_ )
params_ = new ParamList();
params_->insert(params_->begin(), new Param(connection_id->clone(), conn_decl_->DataType()));
AnalyzerDecl::Prepare();
dataunit_->Prepare(env_);
}
void FlowDecl::GenPubDecls(Output* out_h, Output* out_cc) { AnalyzerDecl::GenPubDecls(out_h, out_cc); }
void FlowDecl::GenPrivDecls(Output* out_h, Output* out_cc) {
// Declare the data unit
dataunit_->dataunit_var_field()->GenPrivDecls(out_h, env_);
// Declare the analyzer context
dataunit_->context_var_field()->GenPrivDecls(out_h, env_);
AnalyzerDecl::GenPrivDecls(out_h, out_cc);
}
void FlowDecl::GenInitCode(Output* out_cc) {
AnalyzerDecl::GenInitCode(out_cc);
out_cc->println("%s = nullptr;", env_->LValue(dataunit_id));
out_cc->println("%s = nullptr;", env_->LValue(analyzer_context_id));
if ( dataunit_->type() == AnalyzerDataUnit::FLOWUNIT ) {
flow_buffer_var_field_->type()->GenPreParsing(out_cc, env_);
env_->SetEvaluated(flow_buffer_var_field_->id());
}
}
void FlowDecl::GenCleanUpCode(Output* out_cc) {
GenDeleteDataUnit(out_cc);
AnalyzerDecl::GenCleanUpCode(out_cc);
}
void FlowDecl::GenEOFFunc(Output* out_h, Output* out_cc) {
string proto = strfmt("%s()", kFlowEOF);
out_h->println("void %s;", proto.c_str());
out_cc->println("void %s::%s {", class_name().c_str(), proto.c_str());
out_cc->inc_indent();
foreach (i, AnalyzerHelperList, eof_helpers_) {
(*i)->GenCode(nullptr, out_cc, this);
}
if ( dataunit_->type() == AnalyzerDataUnit::FLOWUNIT ) {
out_cc->println("%s->set_eof();", env_->LValue(flow_buffer_id));
out_cc->println("%s(nullptr, nullptr);", kNewData);
}
out_cc->dec_indent();
out_cc->println("}");
}
void FlowDecl::GenGapFunc(Output* out_h, Output* out_cc) {
string proto = strfmt("%s(int gap_length)", kFlowGap);
out_h->println("void %s;", proto.c_str());
out_cc->println("void %s::%s {", class_name().c_str(), proto.c_str());
out_cc->inc_indent();
if ( dataunit_->type() == AnalyzerDataUnit::FLOWUNIT ) {
out_cc->println("%s->NewGap(gap_length);", env_->LValue(flow_buffer_id));
}
out_cc->dec_indent();
out_cc->println("}");
}
void FlowDecl::GenProcessFunc(Output* out_h, Output* out_cc) {
env_->AddID(begin_of_data, TEMP_VAR, extern_type_const_byteptr);
env_->AddID(end_of_data, TEMP_VAR, extern_type_const_byteptr);
string proto = strfmt("%s(const_byteptr %s, const_byteptr %s)", kNewData, env_->LValue(begin_of_data),
env_->LValue(end_of_data));
out_h->println("void %s override;", proto.c_str());
out_cc->println("void %s::%s {", class_name().c_str(), proto.c_str());
out_cc->inc_indent();
out_cc->println("try {");
out_cc->inc_indent();
env_->SetEvaluated(begin_of_data);
env_->SetEvaluated(end_of_data);
switch ( dataunit_->type() ) {
case AnalyzerDataUnit::DATAGRAM: GenCodeDatagram(out_cc); break;
case AnalyzerDataUnit::FLOWUNIT: GenCodeFlowUnit(out_cc); break;
default: ASSERT(0);
}
out_cc->dec_indent();
out_cc->println("} catch ( binpac::Exception const& e ) {");
out_cc->inc_indent();
GenCleanUpCode(out_cc);
if ( dataunit_->type() == AnalyzerDataUnit::FLOWUNIT ) {
out_cc->println("%s->DiscardData();", env_->LValue(flow_buffer_id));
}
out_cc->println("throw e;");
out_cc->dec_indent();
out_cc->println("}");
out_cc->dec_indent();
out_cc->println("}");
out_cc->println("");
}
void FlowDecl::GenNewDataUnit(Output* out_cc) {
Type* unit_datatype = dataunit_->data_type();
// dataunit_->data_type()->GenPreParsing(out_cc, env_);
dataunit_->GenNewDataUnit(out_cc, env_);
if ( unit_datatype->buffer_input() && unit_datatype->buffer_mode() == Type::BUFFER_BY_LENGTH ) {
out_cc->println("%s->NewFrame(0, false);", env_->LValue(flow_buffer_id));
}
dataunit_->GenNewContext(out_cc, env_);
}
void FlowDecl::GenDeleteDataUnit(Output* out_cc) {
// Do not just delete dataunit, because we may just want to Unref it.
// out_cc->println("delete %s;", env_->LValue(dataunit_id));
dataunit_->data_type()->GenCleanUpCode(out_cc, env_);
dataunit_->context_type()->GenCleanUpCode(out_cc, env_);
}
void FlowDecl::GenCodeFlowUnit(Output* out_cc) {
Type* unit_datatype = dataunit_->data_type();
out_cc->println("%s->NewData(%s, %s);", env_->LValue(flow_buffer_id), env_->RValue(begin_of_data),
env_->RValue(end_of_data));
out_cc->println("while ( %s->data_available() && ", env_->LValue(flow_buffer_id));
out_cc->inc_indent();
out_cc->println("( !%s->have_pending_request() || %s->ready() ) ) {", env_->LValue(flow_buffer_id),
env_->LValue(flow_buffer_id));
// Generate a new dataunit if necessary
out_cc->println("if ( ! %s ) {", env_->LValue(dataunit_id));
out_cc->inc_indent();
out_cc->println("BINPAC_ASSERT(!%s);", env_->LValue(analyzer_context_id));
GenNewDataUnit(out_cc);
out_cc->dec_indent();
out_cc->println("}");
DataPtr data(env_, nullptr, 0);
unit_datatype->GenParseCode(out_cc, env_, data, 0);
out_cc->println("if ( %s ) {", unit_datatype->parsing_complete(env_).c_str());
out_cc->inc_indent();
out_cc->println("// Clean up the flow unit after parsing");
GenDeleteDataUnit(out_cc);
// out_cc->println("BINPAC_ASSERT(%s == 0);", env_->LValue(dataunit_id));
out_cc->dec_indent();
out_cc->println("} else {");
out_cc->inc_indent();
out_cc->println("// Resume upon next input segment");
out_cc->println("BINPAC_ASSERT(!%s->ready());", env_->RValue(flow_buffer_id));
out_cc->println("break;");
out_cc->dec_indent();
out_cc->println("}");
out_cc->dec_indent();
out_cc->println("}");
}
void FlowDecl::GenCodeDatagram(Output* out_cc) {
Type* unit_datatype = dataunit_->data_type();
GenNewDataUnit(out_cc);
string parse_params = strfmt("%s, %s", env_->RValue(begin_of_data), env_->RValue(end_of_data));
if ( RequiresAnalyzerContext::compute(unit_datatype) ) {
parse_params += ", ";
parse_params += env_->RValue(analyzer_context_id);
}
DataPtr dataptr(env_, begin_of_data, 0);
unit_datatype->GenParseCode(out_cc, env_, dataptr, 0);
GenDeleteDataUnit(out_cc);
}

View file

@ -1,48 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef pac_flow_h
#define pac_flow_h
#include "pac_analyzer.h"
class FlowDecl : public AnalyzerDecl {
public:
FlowDecl(ID* flow_id, ParamList* params, AnalyzerElementList* elemlist);
~FlowDecl() override;
void Prepare() override;
void set_conn_decl(ConnDecl* c) { conn_decl_ = c; }
static ParameterizedType* flow_buffer_type();
protected:
void AddBaseClass(vector<string>* base_classes) const override;
void GenInitCode(Output* out_cc) override;
void GenCleanUpCode(Output* out_cc) override;
void GenProcessFunc(Output* out_h, Output* out_cc) override;
void GenEOFFunc(Output* out_h, Output* out_cc) override;
void GenGapFunc(Output* out_h, Output* out_cc) override;
void GenPubDecls(Output* out_h, Output* out_cc) override;
void GenPrivDecls(Output* out_h, Output* out_cc) override;
void ProcessFlowElement(AnalyzerFlow* flow_elem) override;
void ProcessDataUnitElement(AnalyzerDataUnit* dataunit_elem) override;
private:
void GenNewDataUnit(Output* out_cc);
void GenDeleteDataUnit(Output* out_cc);
void GenCodeFlowUnit(Output* out_cc);
void GenCodeDatagram(Output* out_cc);
AnalyzerDataUnit* dataunit_;
ConnDecl* conn_decl_;
Field* flow_buffer_var_field_;
static ParameterizedType* flow_buffer_type_;
};
#endif // pac_flow_h

Some files were not shown because too many files have changed in this diff Show more