PyNerva 0.0.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. nervapy/__init__.py +50 -0
  2. nervapy/abi.py +91 -0
  3. nervapy/arm/__init__.py +124 -0
  4. nervapy/arm/__main__.py +0 -0
  5. nervapy/arm/abi.py +138 -0
  6. nervapy/arm/formats.py +49 -0
  7. nervapy/arm/function.py +2465 -0
  8. nervapy/arm/generic.py +10796 -0
  9. nervapy/arm/instructions.py +519 -0
  10. nervapy/arm/isa.py +409 -0
  11. nervapy/arm/literal_pool.py +331 -0
  12. nervapy/arm/microarchitecture.py +211 -0
  13. nervapy/arm/pseudo.py +652 -0
  14. nervapy/arm/registers.py +1458 -0
  15. nervapy/arm/vfpneon.py +4092 -0
  16. nervapy/arm.py +13 -0
  17. nervapy/c/__init__.py +1 -0
  18. nervapy/c/types.py +436 -0
  19. nervapy/codegen.py +99 -0
  20. nervapy/common/__init__.py +4 -0
  21. nervapy/common/function.py +5 -0
  22. nervapy/common/regalloc.py +121 -0
  23. nervapy/constant_data.py +282 -0
  24. nervapy/encoder.py +246 -0
  25. nervapy/formats/__init__.py +2 -0
  26. nervapy/formats/elf/__init__.py +4 -0
  27. nervapy/formats/elf/file.py +178 -0
  28. nervapy/formats/elf/image.py +106 -0
  29. nervapy/formats/elf/section.py +422 -0
  30. nervapy/formats/elf/symbol.py +281 -0
  31. nervapy/formats/macho/__init__.py +2 -0
  32. nervapy/formats/macho/file.py +123 -0
  33. nervapy/formats/macho/image.py +143 -0
  34. nervapy/formats/macho/section.py +322 -0
  35. nervapy/formats/macho/symbol.py +158 -0
  36. nervapy/formats/mscoff/__init__.py +8 -0
  37. nervapy/formats/mscoff/image.py +132 -0
  38. nervapy/formats/mscoff/section.py +181 -0
  39. nervapy/formats/mscoff/symbol.py +148 -0
  40. nervapy/function.py +136 -0
  41. nervapy/literal.py +731 -0
  42. nervapy/loader.py +188 -0
  43. nervapy/name.py +159 -0
  44. nervapy/parse.py +52 -0
  45. nervapy/stream.py +58 -0
  46. nervapy/util.py +126 -0
  47. nervapy/writer.py +518 -0
  48. nervapy/x86_64/__init__.py +324 -0
  49. nervapy/x86_64/__main__.py +407 -0
  50. nervapy/x86_64/abi.py +517 -0
  51. nervapy/x86_64/amd.py +6464 -0
  52. nervapy/x86_64/avx.py +102029 -0
  53. nervapy/x86_64/crypto.py +1533 -0
  54. nervapy/x86_64/encoding.py +424 -0
  55. nervapy/x86_64/fma.py +19138 -0
  56. nervapy/x86_64/function.py +2707 -0
  57. nervapy/x86_64/generic.py +23384 -0
  58. nervapy/x86_64/instructions.py +500 -0
  59. nervapy/x86_64/isa.py +476 -0
  60. nervapy/x86_64/lower.py +126 -0
  61. nervapy/x86_64/mask.py +2593 -0
  62. nervapy/x86_64/meta.py +143 -0
  63. nervapy/x86_64/mmxsse.py +17265 -0
  64. nervapy/x86_64/nacl.py +327 -0
  65. nervapy/x86_64/operand.py +1204 -0
  66. nervapy/x86_64/options.py +21 -0
  67. nervapy/x86_64/pseudo.py +686 -0
  68. nervapy/x86_64/registers.py +1225 -0
  69. nervapy/x86_64/types.py +17 -0
  70. nervapy/x86_64/uarch.py +580 -0
  71. pynerva-0.0.7.dist-info/METADATA +310 -0
  72. pynerva-0.0.7.dist-info/RECORD +74 -0
  73. pynerva-0.0.7.dist-info/WHEEL +4 -0
  74. pynerva-0.0.7.dist-info/licenses/LICENSE.rst +15 -0
nervapy/loader.py ADDED
@@ -0,0 +1,188 @@
1
+ # This file is part of PeachPy package and is licensed under the Simplified BSD license.
2
+ # See license.rst for the full text of the license.
3
+
4
+ import sys
5
+
6
+
7
+ class Loader:
8
+ def __init__(self, code_size, data_size=0):
9
+ from nervapy.util import is_int
10
+
11
+ if not is_int(code_size):
12
+ raise TypeError("code size must be an integer")
13
+ if not is_int(data_size):
14
+ raise TypeError("data size must be an integer")
15
+ if code_size <= 0:
16
+ raise ValueError("code size must be positive")
17
+ if data_size < 0:
18
+ raise ValueError("data size must be non-negative")
19
+
20
+ import mmap
21
+
22
+ self.allocation_granularity = max(mmap.ALLOCATIONGRANULARITY, mmap.PAGESIZE)
23
+ self.code_address = None
24
+ self.code_size = self.allocation_size(code_size)
25
+ self.data_address = None
26
+ self.data_size = self.allocation_size(data_size)
27
+
28
+ self._release_memory = None
29
+
30
+ osname = sys.platform.lower()
31
+ if (
32
+ osname == "darwin"
33
+ or osname.startswith("linux")
34
+ or osname.startswith("freebsd")
35
+ ):
36
+ import ctypes
37
+
38
+ if osname == "darwin":
39
+ libc = ctypes.cdll.LoadLibrary("libc.dylib")
40
+ elif osname.startswith("freebsd"):
41
+ libc = ctypes.cdll.LoadLibrary("libc.so.7")
42
+ else:
43
+ libc = ctypes.cdll.LoadLibrary("libc.so.6")
44
+
45
+ # void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset)
46
+ mmap_function = libc.mmap
47
+ mmap_function.restype = ctypes.c_void_p
48
+ mmap_function.argtype = [
49
+ ctypes.c_void_p,
50
+ ctypes.c_size_t,
51
+ ctypes.c_int,
52
+ ctypes.c_int,
53
+ ctypes.c_int,
54
+ ctypes.c_size_t,
55
+ ]
56
+ # int munmap(void* addr, size_t len)
57
+ munmap_function = libc.munmap
58
+ munmap_function.restype = ctypes.c_int
59
+ munmap_function.argtype = [ctypes.c_void_p, ctypes.c_size_t]
60
+
61
+ def munmap(address, size):
62
+ munmap_result = munmap_function(ctypes.c_void_p(address), size)
63
+ assert munmap_result == 0
64
+
65
+ self._release_memory = lambda address_size: munmap(
66
+ address_size[0], address_size[1]
67
+ )
68
+
69
+ # Allocate code segment
70
+ code_address = mmap_function(
71
+ None,
72
+ self.code_size,
73
+ mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC,
74
+ mmap.MAP_ANON | mmap.MAP_PRIVATE,
75
+ -1,
76
+ 0,
77
+ )
78
+ if code_address == -1:
79
+ raise OSError("Failed to allocate memory for code segment")
80
+ self.code_address = code_address
81
+
82
+ if self.data_size > 0:
83
+ # Allocate data segment
84
+ data_address = mmap_function(
85
+ None,
86
+ self.data_size,
87
+ mmap.PROT_READ | mmap.PROT_WRITE,
88
+ mmap.MAP_ANON | mmap.MAP_PRIVATE,
89
+ -1,
90
+ 0,
91
+ )
92
+ if data_address == -1:
93
+ raise OSError("Failed to allocate memory for data segment")
94
+ self.data_address = data_address
95
+ elif osname == "win32":
96
+ import ctypes
97
+
98
+ # From WinNT.h
99
+ PAGE_READWRITE = 0x04
100
+ PAGE_EXECUTE_READWRITE = 0x40
101
+ MEM_COMMIT = 0x1000
102
+ MEM_RESERVE = 0x2000
103
+ MEM_RELEASE = 0x8000
104
+
105
+ # LPVOID WINAPI VirtualAlloc(LPVOID address, SIZE_T size, DWORD allocationType, DWORD protect)
106
+ VirtualAlloc_function = ctypes.windll.kernel32.VirtualAlloc
107
+ VirtualAlloc_function.restype = ctypes.c_void_p
108
+ VirtualAlloc_function.argtypes = [
109
+ ctypes.c_void_p,
110
+ ctypes.c_size_t,
111
+ ctypes.c_ulong,
112
+ ctypes.c_ulong,
113
+ ]
114
+ # BOOL WINAPI VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
115
+ VirtualFree_function = ctypes.windll.kernel32.VirtualFree
116
+ VirtualFree_function.restype = ctypes.c_int
117
+ VirtualFree_function.argtypes = [
118
+ ctypes.c_void_p,
119
+ ctypes.c_size_t,
120
+ ctypes.c_ulong,
121
+ ]
122
+
123
+ def VirtualFree(address, size):
124
+ VirtualFree_result = VirtualFree_function(address, 0, MEM_RELEASE)
125
+ assert VirtualFree_result != 0
126
+
127
+ self._release_memory = lambda address_size: VirtualFree(
128
+ address_size[0], address_size[1]
129
+ )
130
+
131
+ # Allocate code segment
132
+ code_address = VirtualAlloc_function(
133
+ None, self.code_size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE
134
+ )
135
+ if not code_address:
136
+ raise OSError("Failed to allocate memory for code segment")
137
+ self.code_address = code_address
138
+
139
+ if self.data_size > 0:
140
+ # Allocate data segment
141
+ data_address = VirtualAlloc_function(
142
+ None, self.data_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE
143
+ )
144
+ if not data_address:
145
+ raise OSError("Failed to allocate memory for data segment")
146
+ self.data_address = data_address
147
+ elif osname == "nacl":
148
+ import dynacl
149
+
150
+ # Allocate code segment
151
+ self.allocation = dynacl.allocate(self.code_size, self.data_size)
152
+ self.code_address = self.allocation.code_address
153
+ self.data_address = self.allocation.data_address
154
+ self.copy_code = self._nacl_copy_code
155
+ else:
156
+ raise ValueError("Unknown host OS: " + osname)
157
+
158
+ def allocation_size(self, segment_size):
159
+ import nervapy.util
160
+
161
+ return nervapy.util.roundup(segment_size, self.allocation_granularity)
162
+
163
+ def copy_code(self, code_segment):
164
+ import ctypes
165
+
166
+ ctypes.memmove(
167
+ self.code_address, ctypes.c_char_p(bytes(code_segment)), len(code_segment)
168
+ )
169
+
170
+ def _nacl_copy_code(self, code_segment):
171
+ code_offset = 0
172
+ self.allocation.dyncode_create(code_segment, code_offset)
173
+
174
+ def copy_data(self, data_segment):
175
+ import ctypes
176
+
177
+ ctypes.memmove(
178
+ self.data_address, ctypes.c_char_p(bytes(data_segment)), len(data_segment)
179
+ )
180
+
181
+ def __del__(self):
182
+ if self._release_memory is not None:
183
+ if self.code_address is not None:
184
+ self._release_memory((self.code_address, self.code_size))
185
+ self.code_address = None
186
+ if self.data_address is not None:
187
+ self._release_memory((self.data_address, self.data_size))
188
+ self.data_address = None
nervapy/name.py ADDED
@@ -0,0 +1,159 @@
1
+ # This file is part of PeachPy package and is licensed under the Simplified BSD license.
2
+ # See license.rst for the full text of the license.
3
+
4
+ import six
5
+
6
+
7
+ class Name:
8
+ def __init__(self, name=None, prename=None):
9
+ assert name is None or isinstance(name, str)
10
+ assert prename is None or isinstance(prename, str)
11
+ assert (
12
+ name is None or prename is None
13
+ ), "Either name or prename, but not both, can be specified"
14
+ self.name = name
15
+ self.prename = prename
16
+
17
+ def __str__(self):
18
+ if self.name is not None:
19
+ return self.name
20
+ elif self.prename is not None:
21
+ return "<" + self.prename + ">"
22
+ else:
23
+ return "<?>"
24
+
25
+ def __repr__(self):
26
+ return str(self)
27
+
28
+ def __hash__(self):
29
+ return hash(self.name) ^ id(self)
30
+
31
+ def __eq__(self, other):
32
+ return isinstance(other, Name) and (self is other or self.name == other.name)
33
+
34
+ def __ne__(self, other):
35
+ return not isinstance(other, Name) or (
36
+ self is not other and self.name != other.name
37
+ )
38
+
39
+ @staticmethod
40
+ def check_name(name):
41
+ """Verifies that the name is appropriate for a symbol"""
42
+ if not isinstance(name, str):
43
+ raise TypeError("Invalid name %s: string required" % str(name))
44
+ import re
45
+
46
+ if not re.match("^[_a-zA-Z]\\w*$", name):
47
+ raise ValueError("Invalid name: " + name)
48
+ if name.startswith("__"):
49
+ raise ValueError(
50
+ "Invalid name %s: names starting with __ are reserved for PeachPy purposes"
51
+ % name
52
+ )
53
+
54
+
55
+ class Namespace:
56
+ def __init__(self, scope_name):
57
+ assert scope_name is None or isinstance(scope_name, Name)
58
+ # Name of the namespace.
59
+ self.scope_name = scope_name
60
+ # Map from name string to either Name or Namespace object
61
+ self.names = dict()
62
+ # Map from prename string to a set of Name and Namespace objects
63
+ self.prenames = dict()
64
+
65
+ def __str__(self):
66
+ return str(self.scope_name)
67
+
68
+ def __hash__(self):
69
+ return hash(self.scope_name)
70
+
71
+ def __eq__(self, other):
72
+ return isinstance(other, Namespace) and self.scope_name == other.scope_name
73
+
74
+ def __ne__(self, other):
75
+ return not isinstance(other, Namespace) or self.scope_name != other.scope_name
76
+
77
+ def add_scoped_name(self, scoped_name):
78
+ assert isinstance(scoped_name, tuple)
79
+ scope_name, subscoped_name = scoped_name[0], scoped_name[1:]
80
+ assert isinstance(scope_name, Name)
81
+ scope = scope_name
82
+ if subscoped_name:
83
+ scope = Namespace(scope_name)
84
+ scope.add_scoped_name(subscoped_name)
85
+ if scope_name.name:
86
+ assert scope_name.prename is None
87
+ if scope_name.name in self.names:
88
+ if subscoped_name and isinstance(
89
+ self.names[scope_name.name], Namespace
90
+ ):
91
+ self.names[scope_name.name].add_scoped_name(subscoped_name)
92
+ else:
93
+ raise ValueError("Name %s already exists" % scope_name.name)
94
+ else:
95
+ self.names[scope_name.name] = scope
96
+ else:
97
+ assert scope_name.name is None
98
+ self.prenames.setdefault(scope_name.prename, set())
99
+ if subscoped_name:
100
+ for subscope in iter(self.prenames[scope_name.prename]):
101
+ if (
102
+ isinstance(subscope, Namespace)
103
+ and subscope.scope_name is scope_name
104
+ ):
105
+ subscope.add_scoped_name(subscoped_name)
106
+ return
107
+ self.prenames[scope_name.prename].add(scope)
108
+
109
+ def assign_names(self):
110
+ # Step 1: assign names to symbols with prenames with no conflicts
111
+ for prename in six.iterkeys(self.prenames):
112
+ if prename is not None:
113
+ if len(self.prenames[prename]) == 1 and prename not in self.names:
114
+ name_object = next(iter(self.prenames[prename]))
115
+ self.names[prename] = name_object
116
+ if isinstance(name_object, Namespace):
117
+ name_object = name_object.scope_name
118
+ name_object.name = prename
119
+
120
+ # Step 2: assign names to symbols with conflicting prenames
121
+ for prename, prename_objects in six.iteritems(self.prenames):
122
+ if prename is not None:
123
+ suffix = 0
124
+ suffixed_name = prename + str(suffix)
125
+ for name_object in iter(prename_objects):
126
+ # Check that the name wasn't already assigned at Step 1
127
+ if name_object.name is None:
128
+ # Generate a non-conflicting name by appending a suffix
129
+ while suffixed_name in self.names:
130
+ suffix += 1
131
+ suffixed_name = prename + str(suffix)
132
+ self.names[suffixed_name] = name_object
133
+ if isinstance(name_object, Namespace):
134
+ name_object = name_object.scope_name
135
+ name_object.name = suffixed_name
136
+
137
+ # Step 3: assign names to symbols without prenames
138
+ if None in self.prenames:
139
+ unnamed_objects = self.prenames[None]
140
+ suffix = 0
141
+ suffixed_name = "__local" + str(suffix)
142
+ for name_object in iter(unnamed_objects):
143
+ # Generate a non-conflicting name by appending a suffix
144
+ while suffixed_name in self.names:
145
+ suffix += 1
146
+ suffixed_name = "__local" + str(suffix)
147
+ self.names[suffixed_name] = name_object
148
+ if isinstance(name_object, Namespace):
149
+ name_object = name_object.scope_name
150
+ name_object.name = suffixed_name
151
+ pass
152
+
153
+ @property
154
+ def name(self):
155
+ return self.scope_name.name
156
+
157
+ @property
158
+ def prename(self):
159
+ return self.scope_name.prename
nervapy/parse.py ADDED
@@ -0,0 +1,52 @@
1
+ # This file is part of PeachPy package and is licensed under the Simplified BSD license.
2
+ # See license.rst for the full text of the license.
3
+
4
+
5
+ def parse_assigned_variable_name(stack_frames, constructor_name):
6
+ """Analyses the provided stack frames and parses Python assignment expressions like
7
+ some.namespace.variable_name = some.module.name.`constructor_name`(...)
8
+ from the caller's call site and returns the name of the variable being assigned as a string.
9
+ If the assignment expression is not found, returns None.
10
+ """
11
+ if isinstance(stack_frames, list) and len(stack_frames) > 1:
12
+ parent_stack_frame = stack_frames[1]
13
+ if isinstance(parent_stack_frame, tuple) and len(parent_stack_frame) == 6:
14
+ _, _, _, _, source_lines, _ = parent_stack_frame
15
+ if isinstance(source_lines, list) and source_lines:
16
+ source_line = source_lines[0]
17
+ if source_line:
18
+ import re
19
+
20
+ assignment_regexp = (
21
+ r"(?:\w+\.)*(\w+)\s*=\s*(?:\w+\.)*"
22
+ + re.escape(constructor_name)
23
+ + r"\(.*\)"
24
+ )
25
+ match = re.match(assignment_regexp, source_line.strip())
26
+ if match:
27
+ return match.group(1)
28
+
29
+
30
+ def parse_with_variable_name(stack_frames, constructor_name):
31
+ """Analyses the provided stack frames and parses Python with expressions like
32
+ with `constructor_name`(...) as variable_name:
33
+ from the caller's call site and returns the name of the variable named in the statement as a string.
34
+ If a with statement is not found, returns None.
35
+ """
36
+ if isinstance(stack_frames, list) and len(stack_frames) > 1:
37
+ parent_stack_frame = stack_frames[1]
38
+ if isinstance(parent_stack_frame, tuple) and len(parent_stack_frame) == 6:
39
+ _, _, _, _, source_lines, _ = parent_stack_frame
40
+ if isinstance(source_lines, list) and source_lines:
41
+ source_line = source_lines[0]
42
+ if source_line:
43
+ import re
44
+
45
+ with_regexp = (
46
+ r"with\s+(?:\w+\.)*"
47
+ + re.escape(constructor_name)
48
+ + r"\(.*\)\s+as\s+([_a-zA-Z]\w*)\s*:"
49
+ )
50
+ match = re.match(with_regexp, source_line.strip())
51
+ if match:
52
+ return match.group(1)
nervapy/stream.py ADDED
@@ -0,0 +1,58 @@
1
+ # This file is part of PeachPy package and is licensed under the Simplified BSD license.
2
+ # See license.rst for the full text of the license.
3
+
4
+ active_stream = None
5
+
6
+
7
+ class InstructionStream(object):
8
+ def __init__(self):
9
+ self.instructions = list()
10
+ self.previous_stream = None
11
+
12
+ def __enter__(self):
13
+ global active_stream
14
+ self.previous_stream = active_stream
15
+ active_stream = self
16
+ return self
17
+
18
+ def __exit__(self, exc_type, exc_value, traceback):
19
+ global active_stream
20
+ active_stream = self.previous_stream
21
+ self.previous_stream = None
22
+
23
+ def __iter__(self):
24
+ return iter(self.instructions)
25
+
26
+ def __len__(self):
27
+ return len(self.instructions)
28
+
29
+ def __getitem__(self, i):
30
+ try:
31
+ return self.instructions[i]
32
+ except IndexError:
33
+ return None
34
+
35
+ def add_instruction(self, instruction):
36
+ if instruction is not None:
37
+ self.instructions.append(instruction)
38
+
39
+ def issue(self, count=1):
40
+ for i in range(count):
41
+ if self.instructions:
42
+ active_stream.add_instruction(self.instructions.pop(0))
43
+
44
+
45
+ class NullStream:
46
+ def __init__(self):
47
+ self.previous_stream = None
48
+
49
+ def __enter__(self):
50
+ global active_stream
51
+ self.previous_stream = active_stream
52
+ active_stream = None
53
+ return self
54
+
55
+ def __exit__(self, exc_type, exc_value, traceback):
56
+ global active_stream
57
+ active_stream = self.previous_stream
58
+ self.previous_stream = None
nervapy/util.py ADDED
@@ -0,0 +1,126 @@
1
+ # This file is part of PeachPy package and is licensed under the Simplified BSD license.
2
+ # See license.rst for the full text of the license.
3
+
4
+ import six
5
+
6
+
7
+ def int_size(n):
8
+ assert is_int64(n)
9
+ if is_int8(n):
10
+ return 1
11
+ elif is_int16(n):
12
+ return 2
13
+ elif is_int32(n):
14
+ return 4
15
+ else:
16
+ return 8
17
+
18
+
19
+ def is_int(n):
20
+ return isinstance(n, six.integer_types)
21
+
22
+
23
+ def is_int64(n):
24
+ return (
25
+ isinstance(n, six.integer_types)
26
+ and -9223372036854775808 <= n <= 18446744073709551615
27
+ )
28
+
29
+
30
+ def is_sint64(n):
31
+ return (
32
+ isinstance(n, six.integer_types)
33
+ and -9223372036854775808 <= n <= 9223372036854775807
34
+ )
35
+
36
+
37
+ def is_uint64(n):
38
+ return isinstance(n, six.integer_types) and 0 <= n <= 18446744073709551615
39
+
40
+
41
+ def is_int32(n):
42
+ return isinstance(n, six.integer_types) and -2147483648 <= n <= 4294967295
43
+
44
+
45
+ def is_sint32(n):
46
+ return isinstance(n, six.integer_types) and -2147483648 <= n <= 2147483647
47
+
48
+
49
+ def is_uint32(n):
50
+ return isinstance(n, six.integer_types) and 0 <= n <= 4294967295
51
+
52
+
53
+ def is_int16(n):
54
+ return isinstance(n, six.integer_types) and -32768 <= n <= 65535
55
+
56
+
57
+ def is_sint16(n):
58
+ return isinstance(n, six.integer_types) and -32768 <= n <= 32767
59
+
60
+
61
+ def is_uint16(n):
62
+ return isinstance(n, six.integer_types) and 0 <= n <= 65535
63
+
64
+
65
+ def is_int8(n):
66
+ return isinstance(n, six.integer_types) and -128 <= n <= 255
67
+
68
+
69
+ def is_uint8(n):
70
+ return isinstance(n, six.integer_types) and 0 <= n <= 255
71
+
72
+
73
+ def is_sint8(n):
74
+ return isinstance(n, six.integer_types) and -128 <= n <= 127
75
+
76
+
77
+ def roundup(n, q):
78
+ import math
79
+
80
+ return int(math.ceil(float(n) / float(q)) * q)
81
+
82
+
83
+ def ilog2(n):
84
+ if n & (n - 1) != 0:
85
+ raise ValueError("%u is not an power of 2" % n)
86
+ if n == 0:
87
+ return 0
88
+ else:
89
+ l = 0
90
+ while n != 1:
91
+ l += 1
92
+ n >>= 1
93
+ return l
94
+
95
+
96
+ def unique(seq):
97
+ seq_values = set()
98
+ unique_seq = []
99
+ for value in seq:
100
+ if value not in seq_values:
101
+ seq_values.add(value)
102
+ unique_seq.append(value)
103
+ return unique_seq
104
+
105
+
106
+ def append_unique(value, sequence=None):
107
+ if sequence is None:
108
+ return [value]
109
+ else:
110
+ if value not in sequence:
111
+ sequence.append(value)
112
+ return sequence
113
+
114
+
115
+ def pairwise(iterable):
116
+ import itertools
117
+
118
+ a, b = itertools.tee(iterable)
119
+ next(b, None)
120
+ return itertools.izip(a, b)
121
+
122
+
123
+ def diff(sequence):
124
+ import operator
125
+
126
+ return map(operator.__sub__, pairwise(sequence))