diff --git a/gcodeutils/gcode_line.pyx b/gcodeutils/gcode_line.pyx
new file mode 100644
index 0000000..0c42526
--- /dev/null
+++ b/gcodeutils/gcode_line.pyx
@@ -0,0 +1,327 @@
+# This file is copied from GCoder.
+#
+# GCoder is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# GCoder is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Printrun. If not, see .
+
+from libc.stdlib cimport malloc, free
+from libc.stdint cimport uint8_t, uint32_t
+from libc.string cimport strlen, strncpy
+
+cdef char*copy_string(object value):
+ cdef char*orig = value
+ str_len = len(orig)
+ cdef char*array = malloc(str_len + 1)
+ strncpy(array, orig, str_len)
+ array[str_len] = 0;
+ return array
+
+cdef enum BitPos:
+ pos_raw = 1 << 0
+ pos_command = 1 << 1
+ pos_is_move = 1 << 2
+ pos_x = 1 << 3
+ pos_y = 1 << 4
+ pos_z = 1 << 5
+ pos_e = 1 << 6
+ pos_f = 1 << 7
+ pos_i = 1 << 8
+ pos_j = 1 << 9
+ pos_relative = 1 << 10
+ pos_relative_e = 1 << 11
+ pos_extruding = 1 << 12
+ pos_current_x = 1 << 13
+ pos_current_y = 1 << 14
+ pos_current_z = 1 << 15
+ pos_current_tool = 1 << 16
+ pos_gcview_end_vertex = 1 << 17
+ pos_current_e = 1 << 18
+ pos_current_f = 1 << 19
+# WARNING: don't use bits 24 to 31 as we store current_tool there
+
+cdef inline uint32_t has_var(uint32_t status, uint32_t pos):
+ return status & pos
+
+cdef inline uint32_t set_has_var(uint32_t status, uint32_t pos):
+ return status | pos
+
+cdef inline uint32_t unset_has_var(uint32_t status, uint32_t pos):
+ return status & ~pos
+
+cdef class GLine:
+ cdef char*_raw
+ cdef char*_command
+ cdef float _x, _y, _z, _e, _f, _i, _j
+ cdef float _current_x, _current_y, _current_z, _current_e, _current_f
+ cdef uint32_t _gcview_end_vertex
+ cdef uint32_t _status
+
+ __slots__ = ()
+
+ def __cinit__(self):
+ self._status = 0
+ self._raw = NULL
+ self._command = NULL
+
+ def __init__(self, line):
+ self.raw = line
+
+ def __dealloc__(self):
+ if self._raw != NULL: free(self._raw)
+ if self._command != NULL: free(self._command)
+
+ property x:
+ def __get__(self):
+ if has_var(self._status, pos_x):
+ return self._x
+ else:
+ return None
+ def __set__(self, value):
+ self._x = value
+ self._status = set_has_var(self._status, pos_x)
+ property y:
+ def __get__(self):
+ if has_var(self._status, pos_y):
+ return self._y
+ else:
+ return None
+ def __set__(self, value):
+ self._y = value
+ self._status = set_has_var(self._status, pos_y)
+ property z:
+ def __get__(self):
+ if has_var(self._status, pos_z):
+ return self._z
+ else:
+ return None
+ def __set__(self, value):
+ self._z = value
+ self._status = set_has_var(self._status, pos_z)
+ property e:
+ def __get__(self):
+ if has_var(self._status, pos_e):
+ return self._e
+ else:
+ return None
+ def __set__(self, value):
+ self._e = value
+ self._status = set_has_var(self._status, pos_e)
+ property current_e:
+ def __get__(self):
+ if has_var(self._status, pos_e):
+ return self._current_e
+ else:
+ return None
+ def __set__(self, value):
+ self._current_e = value
+ self._status = set_has_var(self._status, pos_current_e)
+ property f:
+ def __get__(self):
+ if has_var(self._status, pos_f):
+ return self._f
+ else:
+ return None
+ def __set__(self, value):
+ self._f = value
+ self._status = set_has_var(self._status, pos_f)
+
+ property current_f:
+ def __get__(self):
+ if has_var(self._status, pos_f):
+ return self._current_f
+ else:
+ return None
+ def __set__(self, value):
+ self._current_f = value
+ self._status = set_has_var(self._status, pos_current_f)
+ property i:
+ def __get__(self):
+ if has_var(self._status, pos_i):
+ return self._i
+ else:
+ return None
+ def __set__(self, value):
+ self._i = value
+ self._status = set_has_var(self._status, pos_i)
+ property j:
+ def __get__(self):
+ if has_var(self._status, pos_j):
+ return self._j
+ else:
+ return None
+ def __set__(self, value):
+ self._j = value
+ self._status = set_has_var(self._status, pos_j)
+ property is_move:
+ def __get__(self):
+ if has_var(self._status, pos_is_move):
+ return True
+ else:
+ return False
+ def __set__(self, value):
+ if value:
+ self._status = set_has_var(self._status, pos_is_move)
+ else:
+ self._status = unset_has_var(self._status, pos_is_move)
+ property relative:
+ def __get__(self):
+ if has_var(self._status, pos_relative):
+ return True
+ else:
+ return False
+ def __set__(self, value):
+ if value:
+ self._status = set_has_var(self._status, pos_relative)
+ else:
+ self._status = unset_has_var(self._status, pos_relative)
+ property relative_e:
+ def __get__(self):
+ if has_var(self._status, pos_relative_e):
+ return True
+ else:
+ return False
+ def __set__(self, value):
+ if value:
+ self._status = set_has_var(self._status, pos_relative_e)
+ else:
+ self._status = unset_has_var(self._status, pos_relative_e)
+ property extruding:
+ def __get__(self):
+ if has_var(self._status, pos_extruding):
+ return True
+ else:
+ return False
+ def __set__(self, value):
+ if value:
+ self._status = set_has_var(self._status, pos_extruding)
+ else:
+ self._status = unset_has_var(self._status, pos_extruding)
+ property current_x:
+ def __get__(self):
+ if has_var(self._status, pos_current_x):
+ return self._current_x
+ else:
+ return None
+ def __set__(self, value):
+ self._current_x = value
+ self._status = set_has_var(self._status, pos_current_x)
+ property current_y:
+ def __get__(self):
+ if has_var(self._status, pos_current_y):
+ return self._current_y
+ else:
+ return None
+ def __set__(self, value):
+ self._current_y = value
+ self._status = set_has_var(self._status, pos_current_y)
+ property current_z:
+ def __get__(self):
+ if has_var(self._status, pos_current_z):
+ return self._current_z
+ else:
+ return None
+ def __set__(self, value):
+ self._current_z = value
+ self._status = set_has_var(self._status, pos_current_z)
+ property current_tool:
+ def __get__(self):
+ if has_var(self._status, pos_current_tool):
+ return self._status >> 24
+ else:
+ return None
+ def __set__(self, value):
+ self._status = (self._status & ((1 << 24) - 1)) | (value << 24)
+ self._status = set_has_var(self._status, pos_current_tool)
+ property gcview_end_vertex:
+ def __get__(self):
+ if has_var(self._status, pos_gcview_end_vertex):
+ return self._gcview_end_vertex
+ else:
+ return None
+ def __set__(self, value):
+ self._gcview_end_vertex = value
+ self._status = set_has_var(self._status, pos_gcview_end_vertex)
+ property raw:
+ def __get__(self):
+ if has_var(self._status, pos_raw):
+ return self._raw
+ else:
+ return None
+ def __set__(self, value):
+ # WARNING: memory leak could happen here, as we don't do the following :
+ # if self._raw != NULL: free(self._raw)
+ self._raw = copy_string(value)
+ self._status = set_has_var(self._status, pos_raw)
+ property command:
+ def __get__(self):
+ if has_var(self._status, pos_command):
+ return self._command
+ else:
+ return None
+ def __set__(self, value):
+ # WARNING: memory leak could happen here, as we don't do the following :
+ # if self._command != NULL: free(self._command)
+ self._command = copy_string(value)
+ self._status = set_has_var(self._status, pos_command)
+
+cdef class GLightLine:
+ cdef char*_raw
+ cdef char*_command
+ cdef uint8_t _status
+
+ __slots__ = ()
+
+ def __cinit__(self):
+ self._status = 0
+ self._raw = NULL
+ self._command = NULL
+
+ def __init__(self, line):
+ self.raw = line
+
+ def __dealloc__(self):
+ if self._raw != NULL: free(self._raw)
+ if self._command != NULL: free(self._command)
+
+ property raw:
+ def __get__(self):
+ if has_var(self._status, pos_raw):
+ return self._raw
+ else:
+ return None
+ def __set__(self, value):
+ # WARNING: memory leak could happen here, as we don't do the following :
+ # if self._raw != NULL: free(self._raw)
+ self._raw = copy_string(value)
+ self._status = set_has_var(self._status, pos_raw)
+ property command:
+ def __get__(self):
+ if has_var(self._status, pos_command):
+ return self._command
+ else:
+ return None
+ def __set__(self, value):
+ # WARNING: memory leak could happen here, as we don't do the following :
+ # if self._command != NULL: free(self._command)
+ self._command = copy_string(value)
+ self._status = set_has_var(self._status, pos_command)
+ property is_move:
+ def __get__(self):
+ if has_var(self._status, pos_is_move):
+ return True
+ else:
+ return False
+ def __set__(self, value):
+ if value:
+ self._status = set_has_var(self._status, pos_is_move)
+ else:
+ self._status = unset_has_var(self._status, pos_is_move)
diff --git a/gcodeutils/gcoder.py b/gcodeutils/gcoder.py
index 44bcffc..4571da2 100755
--- a/gcodeutils/gcoder.py
+++ b/gcodeutils/gcoder.py
@@ -105,19 +105,14 @@ def __getattr__(self, name):
# TODO: reenable loading of C optimised representation of GCode
-# try:
-# import gcoder_line
-#
-# Line = gcoder_line.GLine
-# LightLine = gcoder_line.GLightLine
-# except Exception as e:
-# logging.warning("Memory-efficient GCoder implementation unavailable: %s" % e)
-# Line = PyLine
-# LightLine = PyLightLine
-
-Line = PyLine
-LightLine = PyLightLine
-
+try:
+ from gcodeutils import gcode_line
+ Line = gcode_line.GLine
+ LightLine = gcode_line.GLightLine
+except ImportError:
+ logging.warning("Memory-efficient GCoder implementation unavailable")
+ Line = PyLine
+ LightLine = PyLightLine
def find_specific_code(line, code):
exp = specific_exp % code
diff --git a/setup.py b/setup.py
index 6ad014e..33319fb 100644
--- a/setup.py
+++ b/setup.py
@@ -10,6 +10,7 @@
# To use a consistent encoding
from codecs import open
from os import path
+from Cython.Build import cythonize
here = path.abspath(path.dirname(__file__))
@@ -89,6 +90,8 @@
'test': ['nose'],
},
+ ext_modules = cythonize("gcodeutils/gcode_line.pyx"),
+
# If there are data files included in your packages that need to be
# installed, specify them here. If using Python 2.6 or less, then these
# have to be included in MANIFEST.in as well.