<commit_before># -*- coding: utf-8 -*-

"""

ZUGBRUECKE
Calling routines in Windows DLLs from Python scripts running on unixlike systems
https://github.com/pleiszenburg/zugbruecke

	src/zugbruecke/core/data/mem_contents.py: (Un-) packing of memory

	Required to run on platform / side: [UNIX, WINE]

	Copyright (C) 2017-2018 <NAME> <<EMAIL>>

<LICENSE_BLOCK>
The contents of this file are subject to the GNU Lesser General Public License
Version 2.1 ("LGPL" or "License"). You may not use this file except in
compliance with the License. You may obtain a copy of the License at
https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
https://github.com/pleiszenburg/zugbruecke/blob/master/LICENSE

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
specific language governing rights and limitations under the License.
</LICENSE_BLOCK>

"""


# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# IMPORT
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

import ctypes
from pprint import pformat as pf
#import traceback

from ..const import GROUP_VOID
from .memory import (
	generate_pointer_from_bytes,
	is_null_pointer,
	overwrite_pointer_with_bytes,
	serialize_pointer_into_bytes
	)

WCHAR_BYTES = ctypes.sizeof(ctypes.c_wchar)


# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# CLASS: Memory content packing and unpacking
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

class memory_contents_class():


	def apply_memsync_to_argtypes_and_restype_definition(self, memsync_d_list, argtypes_d, restype_d):

		# Iterate over memory segments, which must be kept in sync
		for memsync_d in memsync_d_list:

			# Get type of pointer argument
			arg_type = self.__get_argument_type_by_memsync_path__(memsync_d['p'], argtypes_d, restype_d)

			# HACK make memory sync pointers type agnostic
			arg_type['g'] = GROUP_VOID
			arg_type['t'] = None # no type string


	def client_pack_memory_list(self, args_tuple, memsync_d_list):

		# Pack data for every pointer, append data to package
		return [self.__pack_memory_item__(args_tuple, memsync_d) for memsync_d in memsync_d_list]


	def client_unpack_memory_list(self, args_list, return_value, mem_package_list, memsync_d_list):

		# Iterate over memory package dicts
		for memory_d, memsync_d in zip(mem_package_list, memsync_d_list):

			# If pointer was passed as a Null pointer
			if memory_d['_a'] is None:

				# Unpack one memory section / item
				self.__unpack_memory_item_data__(args_list, memory_d, memsync_d)

			# If pointer pointed to data
			else:

				# Overwrite pointer
				self.__unpack_memory_item_overwrite__(args_list, memory_d, memsync_d)


	def server_pack_memory_list(self, args_list, return_value, mem_package_list, memsync_d_list):

		# Iterate through pointers and serialize them
		for memory_d, memsync_d in zip(mem_package_list, memsync_d_list):

			# If pointer was passed as a Null pointer
			if memory_d['a'] is None:

				memory_d.update(self.__pack_memory_item__(args_list, memsync_d))

			# If pointer pointed to data
			else:

				# Overwrite old data in package with new data from memory
				memory_d['d'] = serialize_pointer_into_bytes(
					ctypes.c_void_p(memory_d['a']), memory_d['l']
					)


	def server_unpack_memory_list(self, args_tuple, arg_memory_list, memsync_d_list):

		# Iterate over memory segments, which must be kept in sync
		for memory_d, memsync_d in zip(arg_memory_list, memsync_d_list):

			# Is this a null pointer?
			if memory_d['a'] is None:

				# Insert new NULL pointer
				self.__unpack_memory_item_null__(args_tuple, memory_d, memsync_d)

			else:

				# Unpack one memory section / item
				self.__unpack_memory_item_data__(args_tuple, memory_d, memsync_d)


	def __adjust_wchar_length__(self, memory_d):

		old_len = memory_d['w']
		new_len = WCHAR_BYTES

		if old_len == new_len:
			return

		tmp = bytearray(memory_d['l'] * new_len // old_len)

		for index in range(old_len if new_len > old_len else new_len):
			tmp[index::new_len] = memory_d['d'][index::old_len]

		memory_d['d'] = bytes(tmp)
		memory_d['l'] = len(memory_d['d'])
		memory_d['w'] = WCHAR_BYTES


	def __get_argument_by_memsync_path__(self, memsync_path, args_tuple):

		# Reference args_tuple as initial value
		element = args_tuple

		# Step through path
		for path_element in memsync_path:

			# Go deeper ...
			if isinstance(path_element, int):
				if path_element < 0: # Pointer to pointer for memory allocation by DLL
					element = self.__item_pointer_strip__(element)
				else: # Dive into argument tuple
					element = element[path_element]
			else: # Dive into struct
				element = getattr(self.__item_pointer_strip__(element), path_element)

		return element


	def __get_argument_type_by_memsync_path__(self, memsync_path, argtypes_d, restype_d):

		# Reference processed argument types - start with depth 0
		arg_type = argtypes_d[memsync_path[0]]

		# Step through path to argument type ...
		for path_element in memsync_path[1:]:
			# Continue on special flags HACK
			if isinstance(path_element, int):
				if path_element < 0:
					continue
			# Go deeper ...
			arg_type = {field['n']: field for field in arg_type['_fields_']}[path_element]
<commit_msg>memsync definition is applied to return type definition<commit_after># -*- coding: utf-8 -*-

"""

ZUGBRUECKE
Calling routines in Windows DLLs from Python scripts running on unixlike systems
https://github.com/pleiszenburg/zugbruecke

	src/zugbruecke/core/data/mem_contents.py: (Un-) packing of memory

	Required to run on platform / side: [UNIX, WINE]

	Copyright (C) 2017-2018 <NAME> <<EMAIL>>

<LICENSE_BLOCK>
The contents of this file are subject to the GNU Lesser General Public License
Version 2.1 ("LGPL" or "License"). You may not use this file except in
compliance with the License. You may obtain a copy of the License at
https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
https://github.com/pleiszenburg/zugbruecke/blob/master/LICENSE

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
specific language governing rights and limitations under the License.
</LICENSE_BLOCK>

"""


# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# IMPORT
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

import ctypes
from pprint import pformat as pf
#import traceback

from ..const import GROUP_VOID
from .memory import (
	generate_pointer_from_bytes,
	is_null_pointer,
	overwrite_pointer_with_bytes,
	serialize_pointer_into_bytes
	)

WCHAR_BYTES = ctypes.sizeof(ctypes.c_wchar)


# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# CLASS: Memory content packing and unpacking
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

class memory_contents_class():


	def apply_memsync_to_argtypes_and_restype_definition(self, memsync_d_list, argtypes_d, restype_d):

		# Iterate over memory segments, which must be kept in sync
		for memsync_d in memsync_d_list:

			# Get type of pointer argument
			arg_type = self.__get_argument_type_by_memsync_path__(memsync_d['p'], argtypes_d, restype_d)

			# HACK make memory sync pointers type agnostic
			arg_type['g'] = GROUP_VOID
			arg_type['t'] = None # no type string


	def client_pack_memory_list(self, args_tuple, memsync_d_list):

		# Pack data for every pointer, append data to package
		return [self.__pack_memory_item__(args_tuple, memsync_d) for memsync_d in memsync_d_list]


	def client_unpack_memory_list(self, args_list, return_value, mem_package_list, memsync_d_list):

		# Iterate over memory package dicts
		for memory_d, memsync_d in zip(mem_package_list, memsync_d_list):

			# If pointer was passed as a Null pointer
			if memory_d['_a'] is None:

				# Unpack one memory section / item
				self.__unpack_memory_item_data__(args_list, memory_d, memsync_d)

			# If pointer pointed to data
			else:

				# Overwrite pointer
				self.__unpack_memory_item_overwrite__(args_list, memory_d, memsync_d)


	def server_pack_memory_list(self, args_list, return_value, mem_package_list, memsync_d_list):

		# Iterate through pointers and serialize them
		for memory_d, memsync_d in zip(mem_package_list, memsync_d_list):

			# If pointer was passed as a Null pointer
			if memory_d['a'] is None:

				memory_d.update(self.__pack_memory_item__(args_list, memsync_d))

			# If pointer pointed to data
			else:

				# Overwrite old data in package with new data from memory
				memory_d['d'] = serialize_pointer_into_bytes(
					ctypes.c_void_p(memory_d['a']), memory_d['l']
					)


	def server_unpack_memory_list(self, args_tuple, arg_memory_list, memsync_d_list):

		# Iterate over memory segments, which must be kept in sync
		for memory_d, memsync_d in zip(arg_memory_list, memsync_d_list):

			# Is this a null pointer?
			if memory_d['a'] is None:

				# Insert new NULL pointer
				self.__unpack_memory_item_null__(args_tuple, memory_d, memsync_d)

			else:

				# Unpack one memory section / item
				self.__unpack_memory_item_data__(args_tuple, memory_d, memsync_d)


	def __adjust_wchar_length__(self, memory_d):

		old_len = memory_d['w']
		new_len = WCHAR_BYTES

		if old_len == new_len:
			return

		tmp = bytearray(memory_d['l'] * new_len // old_len)

		for index in range(old_len if new_len > old_len else new_len):
			tmp[index::new_len] = memory_d['d'][index::old_len]

		memory_d['d'] = bytes(tmp)
		memory_d['l'] = len(memory_d['d'])
		memory_d['w'] = WCHAR_BYTES


	def __get_argument_by_memsync_path__(self, memsync_path, args_tuple):

		# Reference args_tuple as initial value
		element = args_tuple

		# Step through path
		for path_element in memsync_path:

			# Go deeper ...
			if isinstance(path_element, int):
				if path_element < 0: # Pointer to pointer for memory allocation by DLL
					element = self.__item_pointer_strip__(element)
				else: # Dive into argument tuple
					element = element[path_element]
			else: # Dive into struct
				element = getattr(self.__item_pointer_strip__(element), path_element)

		return element


	def __get_argument_type_by_memsync_path__(self, memsync_path, argtypes_d, restype_d):

		# Is path targetting an argument or the return value?
		if isinstance(memsync_path[0], int):
			arg_type = argtypes_d[memsync_path[0]]
		elif memsync_path[0] == 'r':
			arg_type = restype_d
		else:
			raise # TODO

		# Step through path to argument type ...
		for path_element in memsync_path[1:]:
			# Continue on special flags HACK
			if isinstance(path_element, int):
				if path_element < 0:
					continue
			# Go deeper ...
			arg_type = {field['n']: field for field in arg_type['_fields_']}[path_element]

{'fraction_non_alphanumeric': 0.15095695929553418, 'fraction_numerical': 0.0033246473178183125, 'mean_word_length': 4.907643312101911, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 15, 'https://': 6, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5382908', 'n_tokens_mistral': 4039, 'n_tokens_neox': 3513, 'n_words': 987}