comparison configmix/_speedups.c @ 558:7a3c311991d7

An alternate C-implementation of fast_interpolate_variables() that uses the algorithm of the pure-Python implementation
author Franz Glasner <fzglas.hg@dom66.de>
date Wed, 05 Jan 2022 16:02:07 +0100
parents ba8f1295e1e2
children bb160a1e67d7
comparison
equal deleted inserted replaced
557:ba8f1295e1e2 558:7a3c311991d7
689 } 689 }
690 690
691 691
692 static 692 static
693 PyObject * 693 PyObject *
694 fast_interpolate_variables(PyObject *self, PyObject *args) 694 fast_interpolate_variables_old(PyObject *self, PyObject *args)
695 { 695 {
696 PyObject *config; 696 PyObject *config;
697 PyObject *s; 697 PyObject *s;
698 PyObject *cache; 698 PyObject *cache;
699 699
958 } 958 }
959 959
960 960
961 static 961 static
962 PyObject * 962 PyObject *
963 fast_interpolate_variables(PyObject *self, PyObject *args)
964 {
965 PyObject *config;
966 PyObject *s;
967 PyObject *cache;
968
969 Py_ssize_t s_len;
970 Py_ssize_t start, rest, end;
971 PyObject *tmp;
972 PyObject *tmp2;
973 PyObject *result = NULL;
974 PyObject *varname = NULL;
975 PyObject *varvalue = NULL;
976 PyObject *filters = NULL;
977 PyObject *err_type;
978 PyObject *err_value;
979 PyObject *err_tb;
980 int use_cache, cacheable;
981 struct speedups_state *sstate;
982
983 if (!PyArg_UnpackTuple(args, "s", 3, 3, &config, &s, &cache)) {
984 return NULL;
985 }
986 s_len = PyUnicode_GetLength(s); /* also an implicit type check */
987 if (s_len < 0) {
988 return NULL;
989 }
990 if (s_len < 4) {
991 Py_INCREF(s);
992 return s;
993 }
994
995 sstate = PyModule_GetState(self);
996 if (sstate == NULL) {
997 PyErr_SetString(PyExc_RuntimeError, "no module state available");
998 return NULL;
999 }
1000
1001 start = PyUnicode_Find(s, sstate->STARTTOK, 0, s_len, 1);
1002 if (start == -2) {
1003 return NULL;
1004 }
1005 if (start == -1) {
1006 Py_INCREF(s);
1007 return s;
1008 }
1009
1010 result = PyDict_GetItem(cache, s); /* borrowed */
1011 if (result != NULL) {
1012 if (result == sstate->MISSING) {
1013 return PyErr_Format(
1014 PyExc_KeyError,
1015 "Cannot interpolate variables in string %R (cached)",
1016 s);
1017 }
1018 else {
1019 Py_INCREF(result); /* need ownership */
1020 return result;
1021 }
1022 }
1023
1024 result = PyList_New(0);
1025 if (result == 0) {
1026 return NULL;
1027 }
1028
1029 rest = 0;
1030 use_cache = 1;
1031
1032 while (start != -1) {
1033 if (rest < start) {
1034 tmp = PyUnicode_Substring(s, rest, start);
1035 if (tmp == NULL) {
1036 goto error;
1037 }
1038 if (PyList_Append(result, tmp) < 0) {
1039 Py_DECREF(tmp);
1040 goto error;
1041 }
1042 Py_DECREF(tmp); tmp = NULL;
1043 }
1044 end = PyUnicode_Find(s, sstate->ENDTOK, start+2, s_len, 1);
1045 if (end == -2) {
1046 goto error;
1047 }
1048 if (end == -1) {
1049 rest = start;
1050 break;
1051 }
1052
1053 varname = PyUnicode_Substring(s, start+2, end); /* 2 == len(STARTTOK) */
1054 if (varname == NULL) {
1055 goto error;
1056 }
1057
1058 tmp = _fast_split_filters(varname, NULL, sstate);
1059 if (tmp == NULL) {
1060 goto error;
1061 }
1062 if (PyTuple_Size(tmp) != 2) {
1063 PyErr_SetString(PyExc_TypeError, "tuple of size 2 expected");
1064 Py_DECREF(tmp);
1065 goto error;
1066 }
1067 /* Unpack the result tuple */
1068 tmp2 = PyTuple_GetItem(tmp, 0); /* borrowed -- cannot fail */
1069 Py_DECREF(varname);
1070 Py_INCREF(tmp2);
1071 varname = tmp2; tmp2 = NULL;
1072 filters = PyTuple_GetItem(tmp, 1); /* borrowed -- cannot fail */
1073 Py_INCREF(filters);
1074 Py_DECREF(tmp); tmp = NULL;
1075
1076 tmp = PyObject_CallMethod(
1077 config, "_getvar_s_with_cache_info", "O", varname);
1078 if (tmp == NULL) {
1079 if (PyErr_ExceptionMatches(PyExc_KeyError)) {
1080 cacheable = 1;
1081 if (PySequence_Contains(filters, sstate->NONE_FILTER) == 1) {
1082 PyErr_Clear();
1083 Py_INCREF(Py_None);
1084 varvalue = Py_None;
1085 }
1086 else {
1087 if (PySequence_Contains(filters, sstate->EMPTY_FILTER) == 1) {
1088 PyErr_Clear();
1089 Py_INCREF(sstate->EMPTY_STR);
1090 varvalue = sstate->EMPTY_STR;
1091 }
1092 else {
1093 PyErr_Fetch(&err_type, &err_value, &err_tb);
1094 /* this does NOT steal */
1095 PyDict_SetItem(cache, s, sstate->MISSING);
1096 PyErr_Restore(err_type, err_value, err_tb);
1097 goto error;
1098 }
1099 }
1100 }
1101 else {
1102 /* other exception/error than KeyError */
1103 goto error;
1104 }
1105 }
1106 else {
1107 if (PyTuple_Size(tmp) != 2) {
1108 Py_DECREF(tmp);
1109 PyErr_SetString(PyExc_TypeError, "tuple of size 2 expected");
1110 goto error;
1111 }
1112 /* unpack the result */
1113 varvalue = PyTuple_GetItem(tmp, 0); /* borrowed -- but want own */
1114 Py_INCREF(varvalue);
1115 cacheable = PyObject_IsTrue(PyTuple_GetItem(tmp, 1));
1116 Py_DECREF(tmp); tmp = NULL;
1117 }
1118
1119 if (!cacheable) {
1120 use_cache = 0;
1121 }
1122
1123 Py_DECREF(varname); varname = NULL;
1124
1125 tmp = PyObject_CallMethod(
1126 config, "_apply_filters", "OO", filters, varvalue);
1127 if (tmp == NULL) {
1128 goto error;
1129 }
1130 Py_DECREF(varvalue);
1131 varvalue = tmp; tmp = NULL;
1132
1133 Py_DECREF(filters); filters = NULL;
1134
1135 rest = end + 2; /* 2 == len(ENDTOK) */
1136
1137 /*
1138 * Dont apply and type conversions to the variable value if
1139 * the whole `s` is just one expansion
1140 */
1141 if ((start == 0) && (rest == s_len)) {
1142 Py_DECREF(result);
1143 result = varvalue; varvalue = NULL;
1144 goto success; /* break out early */
1145 }
1146
1147 /* Handle None like the empty string */
1148 if (varvalue != Py_None) {
1149 tmp = PyObject_Str(varvalue);
1150 if (tmp == NULL) {
1151 goto error;
1152 }
1153 if (PyList_Append(result, tmp) < 0) {
1154 Py_DECREF(tmp);
1155 goto error;
1156 }
1157 Py_DECREF(tmp); tmp = NULL;
1158 }
1159
1160 /* don't re-evaluate because `self.getvar_s()` expands already */
1161 start = PyUnicode_Find(s, sstate->STARTTOK, rest, s_len, 1);
1162 if (start == -2) {
1163 goto error;
1164 }
1165 }
1166
1167 if (rest < s_len) {
1168 tmp = PyUnicode_Substring(s, rest, s_len);
1169 if (tmp == NULL) {
1170 goto error;
1171 }
1172 if (PyList_Append(result, tmp) < 0) {
1173 Py_DECREF(tmp);
1174 goto error;
1175 }
1176 Py_DECREF(tmp); tmp = NULL;
1177 }
1178
1179 tmp = PyUnicode_Join(sstate->EMPTY_STR, result);
1180 if (tmp == NULL) {
1181 goto error;
1182 }
1183 Py_DECREF(result);
1184 result = tmp; tmp = NULL;
1185
1186 success:
1187 if (use_cache) {
1188 if (PyDict_SetItem(cache, s, result) < 0) {
1189 PyErr_Clear(); /* clear any cache-related error */
1190 }
1191 }
1192 return result;
1193
1194 error:
1195 Py_XDECREF(varname);
1196 Py_XDECREF(varvalue);
1197 Py_XDECREF(filters);
1198 Py_XDECREF(result);
1199 return NULL;
1200 }
1201
1202
1203 static
1204 PyObject *
963 sync_MISSING(PyObject *self, PyObject *missing) 1205 sync_MISSING(PyObject *self, PyObject *missing)
964 { 1206 {
965 struct speedups_state *sstate; 1207 struct speedups_state *sstate;
966 1208
967 sstate = PyModule_GetState(self); 1209 sstate = PyModule_GetState(self);