]> Cypherpunks.ru repositories - pyderasn.git/commitdiff
Faster *Time decoding
authorSergey Matveev <stargrave@stargrave.org>
Sun, 9 Feb 2020 09:06:36 +0000 (12:06 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sun, 9 Feb 2020 09:47:14 +0000 (12:47 +0300)
doc/news.rst
pyderasn.py

index 50c3408fa70e80100875ef8519184899953e1c53..c0e775737d8d22f6da87497b13e6a985164e1690 100644 (file)
@@ -8,6 +8,7 @@ News
 * UTCTime and GeneralizedTime allowed values to have plus sign in them,
   passing int() check successfully. Prohibit that incorrect behaviour
 * UTCTime and GeneralizedTime BER decoding support
+* Faster UTCTime and GeneralizedTime decoding
 * Workability under Cython
 * Explicitly Check that all ObjectIdentifier arcs are non-negative
 
index d637053f53f88e6c2aa72a2014d4b6066f41e201..852091de22a9fe45f4b3c3f08b9f04a2ee7cca50 100755 (executable)
@@ -4164,6 +4164,16 @@ UTCTimeState = namedtuple(
 )
 
 
+def str_to_time_fractions(value):
+    v = pureint(value)
+    year, v = (v // 10**10), (v % 10**10)
+    month, v = (v // 10**8), (v % 10**8)
+    day, v = (v // 10**6), (v % 10**6)
+    hour, v = (v // 10**4), (v % 10**4)
+    minute, second = (v // 100), (v % 100)
+    return year, month, day, hour, minute, second
+
+
 class UTCTime(VisibleString):
     """``UTCTime`` datetime type
 
@@ -4236,18 +4246,12 @@ class UTCTime(VisibleString):
         self.optional = optional
 
     def _strptime_bered(self, value):
-        year = pureint(value[:2])
-        year += 2000 if year < 50 else 1900
-        decoded = datetime(
-            year,  # %Y
-            pureint(value[2:4]),  # %m
-            pureint(value[4:6]),  # %d
-            pureint(value[6:8]),  # %H
-            pureint(value[8:10]),  # %M
-        )
+        year, month, day, hour, minute, _ = str_to_time_fractions(value[:10] + "00")
         value = value[10:]
         if len(value) == 0:
             raise ValueError("no timezone")
+        year += 2000 if year < 50 else 1900
+        decoded = datetime(year, month, day, hour, minute)
         offset = 0
         if value[-1] == "Z":
             value = value[:-1]
@@ -4260,10 +4264,11 @@ class UTCTime(VisibleString):
                 sign = 1
             else:
                 raise ValueError("invalid UTC offset")
-            offset = 60 * pureint(value[-2:])
+            v = pureint(value[-4:])
+            offset, v = (60 * (v % 100)), v // 100
             if offset >= 3600:
                 raise ValueError("invalid UTC offset minutes")
-            offset += 3600 * pureint(value[-4:-2])
+            offset += 3600 * v
             if offset > 14 * 3600:
                 raise ValueError("too big UTC offset")
             offset *= sign
@@ -4275,8 +4280,7 @@ class UTCTime(VisibleString):
         seconds = pureint(value)
         if seconds >= 60:
             raise ValueError("invalid seconds value")
-        decoded += timedelta(seconds=seconds)
-        return offset, decoded
+        return offset, decoded + timedelta(seconds=seconds)
 
     def _strptime(self, value):
         # datetime.strptime's format: %y%m%d%H%M%SZ
@@ -4284,16 +4288,9 @@ class UTCTime(VisibleString):
             raise ValueError("invalid UTCTime length")
         if value[-1] != "Z":
             raise ValueError("non UTC timezone")
-        year = pureint(value[:2])
+        year, month, day, hour, minute, second = str_to_time_fractions(value[:-1])
         year += 2000 if year < 50 else 1900
-        return datetime(
-            year,  # %y
-            pureint(value[2:4]),  # %m
-            pureint(value[4:6]),  # %d
-            pureint(value[6:8]),  # %H
-            pureint(value[8:10]),  # %M
-            pureint(value[10:12]),  # %S
-        )
+        return datetime(year, month, day, hour, minute, second)
 
     def _dt_sanitize(self, value):
         if value.year < 1950 or value.year > 2049:
@@ -4453,14 +4450,9 @@ class GeneralizedTime(UTCTime):
     def _strptime_bered(self, value):
         if len(value) < 4 + 3 * 2:
             raise ValueError("invalid GeneralizedTime")
-        decoded = datetime(
-            pureint(value[:4]),  # %Y
-            pureint(value[4:6]),  # %m
-            pureint(value[6:8]),  # %d
-            pureint(value[8:10]),  # %H
-        )
-        value = value[10:]
-        offset = 0
+        year, month, day, hour, _, _ = str_to_time_fractions(value[:10] + "0000")
+        decoded = datetime(year, month, day, hour)
+        offset, value = 0, value[10:]
         if len(value) == 0:
             return offset, decoded
         if value[-1] == "Z":
@@ -4470,14 +4462,17 @@ class GeneralizedTime(UTCTime):
                 idx = value.rfind(char)
                 if idx == -1:
                     continue
-                offset_raw = value[idx + 1:].replace(":", "")
-                if len(offset_raw) not in (2, 4):
+                offset_raw, value = value[idx + 1:].replace(":", ""), value[:idx]
+                v = pureint(offset_raw)
+                if len(offset_raw) == 4:
+                    offset, v = (60 * (v % 100)), v // 100
+                    if offset >= 3600:
+                        raise ValueError("invalid UTC offset minutes")
+                elif len(offset_raw) == 2:
+                    pass
+                else:
                     raise ValueError("invalid UTC offset")
-                value = value[:idx]
-                offset = 60 * pureint(offset_raw[2:] or "0")
-                if offset >= 3600:
-                    raise ValueError("invalid UTC offset minutes")
-                offset += 3600 * pureint(offset_raw[:2])
+                offset += 3600 * v
                 if offset > 14 * 3600:
                     raise ValueError("too big UTC offset")
                 offset *= sign
@@ -4516,14 +4511,7 @@ class GeneralizedTime(UTCTime):
             # datetime.strptime's format: %Y%m%d%H%M%SZ
             if value[-1] != "Z":
                 raise ValueError("non UTC timezone")
-            return datetime(
-                pureint(value[:4]),  # %Y
-                pureint(value[4:6]),  # %m
-                pureint(value[6:8]),  # %d
-                pureint(value[8:10]),  # %H
-                pureint(value[10:12]),  # %M
-                pureint(value[12:14]),  # %S
-            )
+            return datetime(*str_to_time_fractions(value[:-1]))
         if l >= LEN_YYYYMMDDHHMMSSDMZ:
             # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
             if value[-1] != "Z":
@@ -4537,16 +4525,8 @@ class GeneralizedTime(UTCTime):
             if us_len > 6:
                 raise ValueError("only microsecond fractions are supported")
             us = pureint(us + ("0" * (6 - us_len)))
-            decoded = datetime(
-                pureint(value[:4]),  # %Y
-                pureint(value[4:6]),  # %m
-                pureint(value[6:8]),  # %d
-                pureint(value[8:10]),  # %H
-                pureint(value[10:12]),  # %M
-                pureint(value[12:14]),  # %S
-                us,  # %f
-            )
-            return decoded
+            year, month, day, hour, minute, second = str_to_time_fractions(value[:14])
+            return datetime(year, month, day, hour, minute, second, us)
         raise ValueError("invalid GeneralizedTime length")
 
     def _encode_time(self):