Mercurial > hgrepos > Python > libs > ConfigMix
comparison configmix/_speedups.c @ 603:e55a42144ba9
C-implementations for Configuration.getvarl() and Configuration.getvar_s()
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Tue, 11 Jan 2022 02:50:17 +0100 |
| parents | a2fff0d93d83 |
| children | 776dc94b4ef7 |
comparison
equal
deleted
inserted
replaced
| 602:a2fff0d93d83 | 603:e55a42144ba9 |
|---|---|
| 28 PyObject *EMPTY_FILTER; | 28 PyObject *EMPTY_FILTER; |
| 29 PyObject *NONE_FILTER; | 29 PyObject *NONE_FILTER; |
| 30 PyObject *EMPTY_STR; | 30 PyObject *EMPTY_STR; |
| 31 PyObject *QUOTE_MAP; | 31 PyObject *QUOTE_MAP; |
| 32 PyObject *MISSING; | 32 PyObject *MISSING; |
| 33 PyObject *MARKER; | |
| 33 PyObject *STARTTOK; | 34 PyObject *STARTTOK; |
| 34 PyObject *ENDTOK; | 35 PyObject *ENDTOK; |
| 36 PyObject *REF_NAMESPACE; | |
| 35 }; | 37 }; |
| 38 | |
| 39 | |
| 40 static PyObject * _fast_getvar_s(PyObject *, PyObject *, PyObject *, PyObject *, struct speedups_state *, int*); | |
| 36 | 41 |
| 37 | 42 |
| 38 static | 43 static |
| 39 int | 44 int |
| 40 _hex2ucs4(PyObject *s, Py_ssize_t end, Py_UCS4 *result) | 45 _hex2ucs4(PyObject *s, Py_ssize_t end, Py_UCS4 *result) |
| 833 varname = Py_NewRef(PyTuple_GetItem(tmp, 0)); | 838 varname = Py_NewRef(PyTuple_GetItem(tmp, 0)); |
| 834 /* borrowed -- cannot fail -- want ownership */ | 839 /* borrowed -- cannot fail -- want ownership */ |
| 835 filters = Py_NewRef(PyTuple_GetItem(tmp, 1)); | 840 filters = Py_NewRef(PyTuple_GetItem(tmp, 1)); |
| 836 py_clear_ref(&tmp); | 841 py_clear_ref(&tmp); |
| 837 | 842 |
| 838 tmp = PyObject_CallMethod( | 843 varvalue = _fast_getvar_s(config, varname, NULL, self, sstate, &cacheable); |
| 839 config, "_getvar_s_with_cache_info", "O", varname); | 844 if (varvalue == NULL) { |
| 840 if (tmp == NULL) { | |
| 841 if (PyErr_ExceptionMatches(PyExc_KeyError)) { | 845 if (PyErr_ExceptionMatches(PyExc_KeyError)) { |
| 842 cacheable = 1; | 846 cacheable = 1; |
| 843 if (PySequence_Contains(filters, sstate->NONE_FILTER) == 1) { | 847 if (PySequence_Contains(filters, sstate->NONE_FILTER) == 1) { |
| 844 PyErr_Clear(); | 848 PyErr_Clear(); |
| 845 varvalue = Py_NewRef(Py_None); | 849 varvalue = Py_NewRef(Py_None); |
| 861 else { | 865 else { |
| 862 /* other exception/error than KeyError */ | 866 /* other exception/error than KeyError */ |
| 863 goto error; | 867 goto error; |
| 864 } | 868 } |
| 865 } | 869 } |
| 866 else { | |
| 867 if (PyTuple_Size(tmp) != 2) { | |
| 868 py_clear_ref(&tmp); | |
| 869 PyErr_SetString(PyExc_TypeError, "tuple of size 2 expected"); | |
| 870 goto error; | |
| 871 } | |
| 872 /* unpack the result */ | |
| 873 /* borrowed -- cannot fail -- but want ownership */ | |
| 874 varvalue = Py_NewRef(PyTuple_GetItem(tmp, 0)); | |
| 875 cacheable = PyObject_IsTrue(PyTuple_GetItem(tmp, 1)); | |
| 876 py_clear_ref(&tmp); | |
| 877 } | |
| 878 | 870 |
| 879 if (!cacheable) { | 871 if (!cacheable) { |
| 880 use_cache = 0; | 872 use_cache = 0; |
| 881 } | 873 } |
| 882 | 874 |
| 1063 varname = Py_NewRef(PyTuple_GetItem(tmp, 0)); | 1055 varname = Py_NewRef(PyTuple_GetItem(tmp, 0)); |
| 1064 /* borrowed -- cannot fail -- need ownership */ | 1056 /* borrowed -- cannot fail -- need ownership */ |
| 1065 filters = Py_NewRef(PyTuple_GetItem(tmp, 1)); | 1057 filters = Py_NewRef(PyTuple_GetItem(tmp, 1)); |
| 1066 py_clear_ref(&tmp); | 1058 py_clear_ref(&tmp); |
| 1067 | 1059 |
| 1068 tmp = PyObject_CallMethod( | 1060 varvalue = _fast_getvar_s(config, varname, NULL, self, sstate, &cacheable); |
| 1069 config, "_getvar_s_with_cache_info", "O", varname); | 1061 |
| 1070 if (tmp == NULL) { | 1062 if (varvalue == NULL) { |
| 1071 if (PyErr_ExceptionMatches(PyExc_KeyError)) { | 1063 if (PyErr_ExceptionMatches(PyExc_KeyError)) { |
| 1072 cacheable = 1; | 1064 cacheable = 1; |
| 1073 if (PySequence_Contains(filters, sstate->NONE_FILTER) == 1) { | 1065 if (PySequence_Contains(filters, sstate->NONE_FILTER) == 1) { |
| 1074 PyErr_Clear(); | 1066 PyErr_Clear(); |
| 1075 varvalue = Py_NewRef(Py_None); | 1067 varvalue = Py_NewRef(Py_None); |
| 1093 else { | 1085 else { |
| 1094 /* other exception/error than KeyError */ | 1086 /* other exception/error than KeyError */ |
| 1095 goto error; | 1087 goto error; |
| 1096 } | 1088 } |
| 1097 } | 1089 } |
| 1098 else { | |
| 1099 if (PyTuple_Size(tmp) != 2) { | |
| 1100 py_clear_ref(&tmp); | |
| 1101 PyErr_SetString(PyExc_TypeError, "tuple of size 2 expected"); | |
| 1102 goto error; | |
| 1103 } | |
| 1104 /* unpack the result */ | |
| 1105 /* borrowed -- but want own */ | |
| 1106 varvalue = Py_NewRef(PyTuple_GetItem(tmp, 0)); | |
| 1107 cacheable = PyObject_IsTrue(PyTuple_GetItem(tmp, 1)); | |
| 1108 py_clear_ref(&tmp); | |
| 1109 } | |
| 1110 | 1090 |
| 1111 if (!cacheable) { | 1091 if (!cacheable) { |
| 1112 use_cache = 0; | 1092 use_cache = 0; |
| 1113 } | 1093 } |
| 1114 | 1094 |
| 1189 } | 1169 } |
| 1190 | 1170 |
| 1191 | 1171 |
| 1192 static | 1172 static |
| 1193 PyObject * | 1173 PyObject * |
| 1194 sync_MISSING(PyObject *self, PyObject *missing) | 1174 _fast_getvarl(PyObject *config, PyObject *path, PyObject *namespace, PyObject *default_, struct speedups_state *sstate) |
| 1195 { | 1175 { |
| 1176 PyObject *varvalue; | |
| 1177 PyObject *lookupfn = NULL; | |
| 1178 | |
| 1179 if ((namespace == NULL) || PyObject_Not(namespace)) { | |
| 1180 lookupfn = PyObject_GetAttrString(config, "_lookupvar"); | |
| 1181 if (lookupfn == NULL) { | |
| 1182 goto error; | |
| 1183 } | |
| 1184 } | |
| 1185 else { | |
| 1186 int ns_equals_ref = PyUnicode_Compare(namespace, sstate->REF_NAMESPACE); | |
| 1187 if ((ns_equals_ref < 0) && PyErr_Occurred()) { | |
| 1188 return NULL; | |
| 1189 } | |
| 1190 if (ns_equals_ref == 0) { | |
| 1191 lookupfn = PyObject_GetAttrString(config, "_lookupref"); | |
| 1192 if (lookupfn == NULL) { | |
| 1193 goto error; | |
| 1194 } | |
| 1195 } | |
| 1196 else { | |
| 1197 /* lookup_varns */ | |
| 1198 /* this is borrowed */ | |
| 1199 PyObject *cfg_vars_mod = PyImport_AddModule("configmix.variables"); | |
| 1200 if (cfg_vars_mod == NULL) { | |
| 1201 goto error; | |
| 1202 } | |
| 1203 lookupfn = PyObject_CallMethod(cfg_vars_mod, "lookup_varns", "O", namespace); | |
| 1204 if (lookupfn == NULL) { | |
| 1205 goto handle_possible_keyerror; | |
| 1206 } | |
| 1207 } | |
| 1208 } | |
| 1209 varvalue = PyObject_CallObject(lookupfn, path); | |
| 1210 if (varvalue == NULL) { | |
| 1211 goto handle_possible_keyerror; | |
| 1212 } | |
| 1213 Py_DECREF(lookupfn); | |
| 1214 return varvalue; | |
| 1215 | |
| 1216 handle_possible_keyerror: | |
| 1217 if (PyErr_ExceptionMatches(PyExc_KeyError)) { | |
| 1218 if ((default_ == NULL) || py_object_is(default_, sstate->MARKER)) { | |
| 1219 PyErr_Format(PyExc_KeyError, "Variable %R not found", path); | |
| 1220 /* fall through */ | |
| 1221 } | |
| 1222 else { | |
| 1223 PyErr_Clear(); | |
| 1224 Py_XDECREF(lookupfn); | |
| 1225 return Py_NewRef(default_); | |
| 1226 } | |
| 1227 } | |
| 1228 /* fall through */ | |
| 1229 error: | |
| 1230 Py_XDECREF(lookupfn); | |
| 1231 return NULL; | |
| 1232 } | |
| 1233 | |
| 1234 | |
| 1235 static | |
| 1236 PyObject * | |
| 1237 fast_getvarl(PyObject *self, PyObject *args, PyObject *kwds) | |
| 1238 { | |
| 1239 static char *kwlist[] = {"config", "path", "namespace", "default", NULL}; | |
| 1240 PyObject *path; | |
| 1241 PyObject *config; | |
| 1242 PyObject *namespace = NULL; | |
| 1243 PyObject *default_ = NULL; | |
| 1244 | |
| 1196 struct speedups_state *sstate; | 1245 struct speedups_state *sstate; |
| 1197 | 1246 |
| 1247 if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO!|$OO", kwlist, &config, &PyTuple_Type, &path, &namespace, &default_)) { | |
| 1248 | |
| 1249 return NULL; | |
| 1250 } | |
| 1198 sstate = PyModule_GetState(self); | 1251 sstate = PyModule_GetState(self); |
| 1199 if (sstate == NULL) { | 1252 if (sstate == NULL) { |
| 1200 PyErr_SetString(PyExc_RuntimeError, "no module state available"); | 1253 PyErr_SetString(PyExc_RuntimeError, "no module state available"); |
| 1201 return NULL; | 1254 return NULL; |
| 1202 } | 1255 } |
| 1256 return _fast_getvarl(config, path, namespace, default_, sstate); | |
| 1257 } | |
| 1258 | |
| 1259 | |
| 1260 /** | |
| 1261 * Mixture of py_getvar_s and _py_getvar_s_with_cache_info | |
| 1262 */ | |
| 1263 static | |
| 1264 PyObject * | |
| 1265 _fast_getvar_s(PyObject *config, PyObject *varname, PyObject *default_, PyObject *self, struct speedups_state *sstate, int *cacheable) | |
| 1266 { | |
| 1267 PyObject *varname_b; /* always borrowed */ | |
| 1268 PyObject *namespace_b; /* always borrowed */ | |
| 1269 PyObject *splitted = NULL; | |
| 1270 PyObject *res = NULL; | |
| 1271 PyObject *tmp1; | |
| 1272 PyObject *tmp2; | |
| 1273 | |
| 1274 splitted = fast_split_ns(self, varname); | |
| 1275 if (splitted == NULL) { | |
| 1276 goto error; | |
| 1277 } | |
| 1278 namespace_b = PyTuple_GetItem(splitted, 0); /* borrowed */ | |
| 1279 varname_b = PyTuple_GetItem(splitted, 1); /* borrowed */ | |
| 1280 | |
| 1281 if (PyObject_Not(namespace_b)) { | |
| 1282 tmp1 = _fast_pathstr2path(varname_b, NULL, sstate); | |
| 1283 if (tmp1 == NULL) { | |
| 1284 goto error; | |
| 1285 } | |
| 1286 tmp2 = _fast_getvarl(config, tmp1, NULL, default_, sstate); | |
| 1287 if (tmp2 == NULL) { | |
| 1288 py_clear_ref(&tmp1); | |
| 1289 goto handle_possible_keyerror; | |
| 1290 } | |
| 1291 py_clear_ref(&tmp1); | |
| 1292 res = PyObject_CallMethod(config, "substitute_variables_in_obj", "O", tmp2); | |
| 1293 if (res == NULL) { | |
| 1294 py_clear_ref(&tmp2); | |
| 1295 goto handle_possible_keyerror; | |
| 1296 } | |
| 1297 py_clear_ref(&tmp2); | |
| 1298 /* no namespace -> cacheable */ | |
| 1299 *cacheable = 1; | |
| 1300 } | |
| 1301 else { | |
| 1302 tmp1 = PyTuple_New(1); | |
| 1303 if (tmp1 == NULL) { | |
| 1304 goto error; | |
| 1305 } | |
| 1306 PyTuple_SetItem(tmp1, 0, Py_NewRef(varname_b)); | |
| 1307 tmp2 = _fast_getvarl(config, tmp1, namespace_b, NULL, sstate); | |
| 1308 if (tmp2 == NULL) { | |
| 1309 py_clear_ref(&tmp1); | |
| 1310 goto handle_possible_keyerror; | |
| 1311 } | |
| 1312 py_clear_ref(&tmp1); | |
| 1313 res = PyObject_CallMethod(config, "substitute_variables_in_obj", "O", tmp2); | |
| 1314 if (res == NULL) { | |
| 1315 py_clear_ref(&tmp2); | |
| 1316 goto handle_possible_keyerror; | |
| 1317 } | |
| 1318 py_clear_ref(&tmp2); | |
| 1319 /* results from namespaced lookups are currently not cacheable */ | |
| 1320 *cacheable = 0; | |
| 1321 } | |
| 1322 | |
| 1323 /* free splitted last because of using borrowed refs from it */ | |
| 1324 Py_DECREF(splitted); | |
| 1325 return res; | |
| 1326 | |
| 1327 handle_possible_keyerror: | |
| 1328 if (PyErr_ExceptionMatches(PyExc_KeyError)) { | |
| 1329 if ((default_ == NULL) || py_object_is(default_, sstate->MARKER)) { | |
| 1330 /* fall through */ | |
| 1331 } | |
| 1332 else { | |
| 1333 PyErr_Clear(); | |
| 1334 Py_XDECREF(res); | |
| 1335 Py_XDECREF(splitted); | |
| 1336 return Py_NewRef(default_); | |
| 1337 } | |
| 1338 } | |
| 1339 /* fall through */ | |
| 1340 | |
| 1341 error: | |
| 1342 Py_XDECREF(res); | |
| 1343 Py_XDECREF(splitted); | |
| 1344 return NULL; | |
| 1345 } | |
| 1346 | |
| 1347 | |
| 1348 static | |
| 1349 PyObject * | |
| 1350 fast_getvar_s(PyObject *self, PyObject *args) | |
| 1351 { | |
| 1352 PyObject *config; | |
| 1353 PyObject *varname; | |
| 1354 PyObject *default_; | |
| 1355 int cacheable; | |
| 1356 struct speedups_state *sstate; | |
| 1357 | |
| 1358 if (!PyArg_UnpackTuple(args, "config", 3, 3, &config, &varname, &default_)) { | |
| 1359 return NULL; | |
| 1360 } | |
| 1361 | |
| 1362 sstate = PyModule_GetState(self); | |
| 1363 if (sstate == NULL) { | |
| 1364 PyErr_SetString(PyExc_RuntimeError, "no module state available"); | |
| 1365 return NULL; | |
| 1366 } | |
| 1367 return _fast_getvar_s(config, varname, default_, self, sstate, &cacheable); | |
| 1368 } | |
| 1369 | |
| 1370 | |
| 1371 static | |
| 1372 PyObject * | |
| 1373 sync_MISSING(PyObject *self, PyObject *missing) | |
| 1374 { | |
| 1375 struct speedups_state *sstate; | |
| 1376 | |
| 1377 sstate = PyModule_GetState(self); | |
| 1378 if (sstate == NULL) { | |
| 1379 PyErr_SetString(PyExc_RuntimeError, "no module state available"); | |
| 1380 return NULL; | |
| 1381 } | |
| 1203 if (sstate->MISSING != NULL) { | 1382 if (sstate->MISSING != NULL) { |
| 1204 PyErr_SetString(PyExc_RuntimeError, "_MISSING already set"); | 1383 PyErr_SetString(PyExc_RuntimeError, "_MISSING already set"); |
| 1205 return NULL; | 1384 return NULL; |
| 1206 } | 1385 } |
| 1207 sstate->MISSING = Py_NewRef(missing); | 1386 sstate->MISSING = Py_NewRef(missing); |
| 1387 Py_RETURN_NONE; | |
| 1388 } | |
| 1389 | |
| 1390 | |
| 1391 static | |
| 1392 PyObject * | |
| 1393 sync_MARKER(PyObject *self, PyObject *marker) | |
| 1394 { | |
| 1395 struct speedups_state *sstate; | |
| 1396 | |
| 1397 sstate = PyModule_GetState(self); | |
| 1398 if (sstate == NULL) { | |
| 1399 PyErr_SetString(PyExc_RuntimeError, "no module state available"); | |
| 1400 return NULL; | |
| 1401 } | |
| 1402 if (sstate->MARKER != NULL) { | |
| 1403 PyErr_SetString(PyExc_RuntimeError, "_MARKER already set"); | |
| 1404 return NULL; | |
| 1405 } | |
| 1406 sstate->MARKER = Py_NewRef(marker); | |
| 1208 Py_RETURN_NONE; | 1407 Py_RETURN_NONE; |
| 1209 } | 1408 } |
| 1210 | 1409 |
| 1211 | 1410 |
| 1212 static struct PyMethodDef speedups_methods[] = { | 1411 static struct PyMethodDef speedups_methods[] = { |
| 1214 {"fast_quote", fast_quote, METH_O, PyDoc_STR("C-implementation of configmix.quote")}, | 1413 {"fast_quote", fast_quote, METH_O, PyDoc_STR("C-implementation of configmix.quote")}, |
| 1215 {"fast_pathstr2path", fast_pathstr2path, METH_O, PyDoc_STR("C-implementation of configmix.pathstr2path")}, | 1414 {"fast_pathstr2path", fast_pathstr2path, METH_O, PyDoc_STR("C-implementation of configmix.pathstr2path")}, |
| 1216 {"_fast_split_filters", fast_split_filters, METH_O, PyDoc_STR("C-implementation of configmix.config._split_filters")}, | 1415 {"_fast_split_filters", fast_split_filters, METH_O, PyDoc_STR("C-implementation of configmix.config._split_filters")}, |
| 1217 {"_fast_split_ns", fast_split_ns, METH_O, PyDoc_STR("C-implementation of configmix.config._split_ns")}, | 1416 {"_fast_split_ns", fast_split_ns, METH_O, PyDoc_STR("C-implementation of configmix.config._split_ns")}, |
| 1218 {"_fast_interpolate_variables", fast_interpolate_variables, METH_VARARGS, PyDoc_STR("C-implementation of configmix.config.Configuration.interpolate_variables")}, | 1417 {"_fast_interpolate_variables", fast_interpolate_variables, METH_VARARGS, PyDoc_STR("C-implementation of configmix.config.Configuration.interpolate_variables")}, |
| 1418 {"_fast_getvarl", (PyCFunction)fast_getvarl, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("C-Implementation of configmix.config.Configuration.getvarl")}, | |
| 1419 {"_fast_getvar_s", fast_getvar_s, METH_VARARGS, PyDoc_STR("C-Implementation of configmix.config.Configuration.getvar_s")}, | |
| 1219 {"_sync_MISSING", sync_MISSING, METH_O, PyDoc_STR("Internal function to easily sync the _MISSING object with configmix.config")}, | 1420 {"_sync_MISSING", sync_MISSING, METH_O, PyDoc_STR("Internal function to easily sync the _MISSING object with configmix.config")}, |
| 1220 | 1421 {"_sync_MARKER", sync_MARKER, METH_O, PyDoc_STR("Internal function to easily sync the _MARKER object with configmix.config")}, |
| 1221 {NULL, NULL, 0, NULL} | 1422 {NULL, NULL, 0, NULL} |
| 1222 }; | 1423 }; |
| 1223 | 1424 |
| 1224 #define STRINGIFY(s) #s | 1425 #define STRINGIFY(s) #s |
| 1225 #define XSTRINGIFY(s) STRINGIFY(s) | 1426 #define XSTRINGIFY(s) STRINGIFY(s) |
| 1312 if (sstate->ENDTOK == NULL) { | 1513 if (sstate->ENDTOK == NULL) { |
| 1313 return -1; | 1514 return -1; |
| 1314 } | 1515 } |
| 1315 PyUnicode_InternInPlace(&(sstate->ENDTOK)); | 1516 PyUnicode_InternInPlace(&(sstate->ENDTOK)); |
| 1316 | 1517 |
| 1518 sstate->REF_NAMESPACE = PyUnicode_FromStringAndSize("ref", 3); | |
| 1519 if (sstate->REF_NAMESPACE == NULL) { | |
| 1520 return -1; | |
| 1521 } | |
| 1522 PyUnicode_InternInPlace(&(sstate->REF_NAMESPACE)); | |
| 1523 | |
| 1317 return 0; | 1524 return 0; |
| 1318 } | 1525 } |
| 1319 | 1526 |
| 1320 | 1527 |
| 1321 static | 1528 static |
| 1332 Py_VISIT(sstate->EMPTY_FILTER); | 1539 Py_VISIT(sstate->EMPTY_FILTER); |
| 1333 Py_VISIT(sstate->NONE_FILTER); | 1540 Py_VISIT(sstate->NONE_FILTER); |
| 1334 Py_VISIT(sstate->EMPTY_STR); | 1541 Py_VISIT(sstate->EMPTY_STR); |
| 1335 Py_VISIT(sstate->QUOTE_MAP); | 1542 Py_VISIT(sstate->QUOTE_MAP); |
| 1336 Py_VISIT(sstate->MISSING); | 1543 Py_VISIT(sstate->MISSING); |
| 1544 Py_VISIT(sstate->MARKER); | |
| 1337 Py_VISIT(sstate->STARTTOK); | 1545 Py_VISIT(sstate->STARTTOK); |
| 1338 Py_VISIT(sstate->ENDTOK); | 1546 Py_VISIT(sstate->ENDTOK); |
| 1547 Py_VISIT(sstate->REF_NAMESPACE); | |
| 1339 } | 1548 } |
| 1340 return 0; | 1549 return 0; |
| 1341 } | 1550 } |
| 1342 | 1551 |
| 1343 | 1552 |
| 1355 Py_CLEAR(sstate->EMPTY_FILTER); | 1564 Py_CLEAR(sstate->EMPTY_FILTER); |
| 1356 Py_CLEAR(sstate->NONE_FILTER); | 1565 Py_CLEAR(sstate->NONE_FILTER); |
| 1357 Py_CLEAR(sstate->EMPTY_STR); | 1566 Py_CLEAR(sstate->EMPTY_STR); |
| 1358 Py_CLEAR(sstate->QUOTE_MAP); | 1567 Py_CLEAR(sstate->QUOTE_MAP); |
| 1359 Py_CLEAR(sstate->MISSING); | 1568 Py_CLEAR(sstate->MISSING); |
| 1569 Py_CLEAR(sstate->MARKER); | |
| 1360 Py_CLEAR(sstate->STARTTOK); | 1570 Py_CLEAR(sstate->STARTTOK); |
| 1361 Py_CLEAR(sstate->ENDTOK); | 1571 Py_CLEAR(sstate->ENDTOK); |
| 1572 Py_CLEAR(sstate->REF_NAMESPACE); | |
| 1362 } | 1573 } |
| 1363 return 0; | 1574 return 0; |
| 1364 } | 1575 } |
| 1365 | 1576 |
| 1366 | 1577 |
