// PD_TRACE_DECLARE_FUNCTION ( SDB_RTNLOCALLOBSTREAM__ROLLBACK, "_rtnLocalLobStream::_rooback" ) INT32 _rtnLocalLobStream::_rollback( _pmdEDUCB *cb ) { INT32 rc = SDB_OK ; PD_TRACE_ENTRY( SDB_RTNLOCALLOBSTREAM__ROLLBACK ) ; dmsLobRecord piece ; INT32 num = _getSequence( curOffset() ) ; while ( 0 < num ) { --num ; piece.set( &getOID(), num, 0, 0, NULL ) ; rc = _su->lob()->remove( piece, _mbContext, cb, _getDPSCB() ) ; if ( SDB_OK != rc ) { PD_LOG( PDERROR, "Failed to remove lob[%s]," "sequence:%d, rc:%d", piece._oid->str().c_str(), piece._sequence, rc ) ; if ( SDB_LOB_SEQUENCE_NOT_EXIST != rc ) { goto error ; } rc = SDB_OK ; } } done: PD_TRACE_EXITRC( SDB_RTNLOCALLOBSTREAM__ROLLBACK, rc ) ; return rc ; error: goto done ; }
unsigned H264or5VideoStreamParser::parse() { static int aaaa = 0 ; aaaa++; qDebug()<<"H264or5VideoStreamParser::parse() { 868 aaaa" <<aaaa; try { // The stream must start with a 0x00000001: if (!fHaveSeenFirstStartCode) { // Skip over any input bytes that precede the first 0x00000001: u_int32_t first4Bytes; while ((first4Bytes = test4Bytes()) != 0x00000001) { get1Byte(); setParseState(); // ensures that we progress over bad data } skipBytes(4); // skip this initial code setParseState(); fHaveSeenFirstStartCode = True; // from now on } if (fOutputStartCodeSize > 0 && curFrameSize() == 0 && !haveSeenEOF()) { // Include a start code in the output: save4Bytes(0x00000001); } // Then save everything up until the next 0x00000001 (4 bytes) or 0x000001 (3 bytes), or we hit EOF. // Also make note of the first byte, because it contains the "nal_unit_type": if (haveSeenEOF()) { // We hit EOF the last time that we tried to parse this data, so we know that any remaining unparsed data // forms a complete NAL unit, and that there's no 'start code' at the end: unsigned remainingDataSize = totNumValidBytes() - curOffset(); #ifdef DEBUG unsigned const trailingNALUnitSize = remainingDataSize; #endif while (remainingDataSize > 0) { u_int8_t nextByte = get1Byte(); if (!fHaveSeenFirstByteOfNALUnit) { fFirstByteOfNALUnit = nextByte; fHaveSeenFirstByteOfNALUnit = True; } saveByte(nextByte); --remainingDataSize; } #ifdef DEBUG if (fHNumber == 264) { u_int8_t nal_ref_idc = (fFirstByteOfNALUnit&0x60)>>5; u_int8_t nal_unit_type = fFirstByteOfNALUnit&0x1F; fprintf(stderr, "Parsed trailing %d-byte NAL-unit (nal_ref_idc: %d, nal_unit_type: %d (\"%s\"))\n", trailingNALUnitSize, nal_ref_idc, nal_unit_type, nal_unit_type_description_h264[nal_unit_type]); } else { // 265 u_int8_t nal_unit_type = (fFirstByteOfNALUnit&0x7E)>>1; fprintf(stderr, "Parsed trailing %d-byte NAL-unit (nal_unit_type: %d (\"%s\"))\n", trailingNALUnitSize, nal_unit_type, nal_unit_type_description_h265[nal_unit_type]); } #endif (void)get1Byte(); // forces another read, which will cause EOF to get handled for real this time return 0; } else {
//Render void TileMapScene::Render( Point2Df& offset, const Viewport& viewport ) { // If there is no map set, we can either assert, or we can ignore this // and pretend the developer had other plans. We'll give the developer // the benefit of the doubt, and just return from this method without // doing anything... if ( mPrimaryTileSet == 0 ) return; glEnable( GL_TEXTURE_2D ); // Go over the tile maps and calculate their offsets, then render the map. TileSetMultiSet::const_iterator mapIter = mTileSets.begin(); Point2D viewSize = viewport.GetSize(); Point2D primaryMapSize = mPrimaryTileSet->GetMapSize(); offset.x = Math::Clamp( offset.x, viewSize.x - primaryMapSize.x, 0 ); offset.y = Math::Clamp( offset.y, viewSize.y - primaryMapSize.y, 0 ); for ( mapIter; mapIter != mTileSets.end(); mapIter++ ) { // Calculate the ratio between the current map and the primary map Point2D curMapSize = mapIter->GetMapSize(); Real ratioX = 1.0, ratioY = 1.0; if ( curMapSize.x != viewSize.x && primaryMapSize.x != viewSize.x ) ratioX = ( primaryMapSize.x - viewSize.x ) / Real( curMapSize.x - viewSize.x ); if ( curMapSize.y != viewSize.y && primaryMapSize.y != viewSize.y ) ratioY = ( primaryMapSize.y - viewSize.y ) / Real( curMapSize.y - viewSize.y ); // Calculate the offset of the current map using the offset for the // primary map and the ratios Point2Df curOffset( offset.x / ratioX, offset.y / ratioY ); curOffset.y = Math::Clamp( curOffset.y, viewSize.y - curMapSize.y, 0 ); mapIter->Render( curOffset, viewport ); } glDisable( GL_TEXTURE_2D ); }
unsigned H264VideoStreamParser::parse() { try { // The stream must start with a 0x00000001: if (!fHaveSeenFirstStartCode) { // Skip over any input bytes that precede the first 0x00000001: u_int32_t first4Bytes; while ((first4Bytes = test4Bytes()) != 0x00000001) { get1Byte(); setParseState(); // ensures that we progress over bad data } skipBytes(4); // skip this initial code setParseState(); fHaveSeenFirstStartCode = True; // from now on } if (fOutputStartCodeSize > 0 && curFrameSize() == 0 && !haveSeenEOF()) { // Include a start code in the output: save4Bytes(0x00000001); } // Then save everything up until the next 0x00000001 (4 bytes) or 0x000001 (3 bytes), or we hit EOF. // Also make note of the first byte, because it contains the "nal_unit_type": if (haveSeenEOF()) { // We hit EOF the last time that we tried to parse this data, so we know that any remaining unparsed data // forms a complete NAL unit, and that there's no 'start code' at the end: unsigned remainingDataSize = totNumValidBytes() - curOffset(); #ifdef DEBUG unsigned const trailingNALUnitSize = remainingDataSize; #endif while (remainingDataSize > 0) { u_int8_t nextByte = get1Byte(); if (!fHaveSeenFirstByteOfNALUnit) { fFirstByteOfNALUnit = nextByte; fHaveSeenFirstByteOfNALUnit = True; } saveByte(nextByte); --remainingDataSize; } #ifdef DEBUG u_int8_t nal_ref_idc = (fFirstByteOfNALUnit&0x60)>>5; u_int8_t nal_unit_type = fFirstByteOfNALUnit&0x1F; fprintf(stderr, "Parsed trailing %d-byte NAL-unit (nal_ref_idc: %d, nal_unit_type: %d (\"%s\"))\n", trailingNALUnitSize, nal_ref_idc, nal_unit_type, nal_unit_type_description[nal_unit_type]); #endif (void)get1Byte(); // forces another read, which will cause EOF to get handled for real this time return 0; } else { u_int32_t next4Bytes = test4Bytes(); if (!fHaveSeenFirstByteOfNALUnit) { fFirstByteOfNALUnit = next4Bytes>>24; fHaveSeenFirstByteOfNALUnit = True; } while (next4Bytes != 0x00000001 && (next4Bytes&0xFFFFFF00) != 0x00000100) { // We save at least some of "next4Bytes". if ((unsigned)(next4Bytes&0xFF) > 1) { // Common case: 0x00000001 or 0x000001 definitely doesn't begin anywhere in "next4Bytes", so we save all of it: save4Bytes(next4Bytes); skipBytes(4); } else { // Save the first byte, and continue testing the rest: saveByte(next4Bytes>>24); skipBytes(1); } setParseState(); // ensures forward progress next4Bytes = test4Bytes(); } // Assert: next4Bytes starts with 0x00000001 or 0x000001, and we've saved all previous bytes (forming a complete NAL unit). // Skip over these remaining bytes, up until the start of the next NAL unit: if (next4Bytes == 0x00000001) { skipBytes(4); } else { skipBytes(3); } }
void MatroskaFileParser::parseBlock() { #ifdef DEBUG fprintf(stderr, "parsing SimpleBlock or Block\n"); #endif do { unsigned blockStartPos = curOffset(); // The block begins with the track number: EBMLNumber trackNumber; if (!parseEBMLNumber(trackNumber)) break; fBlockTrackNumber = (unsigned)trackNumber.val(); // If this track is not being read, then skip the rest of this block, and look for another one: if (fOurDemux->lookupDemuxedTrack(fBlockTrackNumber) == NULL) { unsigned headerBytesSeen = curOffset() - blockStartPos; if (headerBytesSeen < fBlockSize) { skipBytes(fBlockSize - headerBytesSeen); } #ifdef DEBUG fprintf(stderr, "\tSkipped block for unused track number %d\n", fBlockTrackNumber); #endif fCurrentParseState = LOOKING_FOR_BLOCK; setParseState(); return; } MatroskaTrack* track = fOurFile.lookup(fBlockTrackNumber); if (track == NULL) break; // shouldn't happen // The next two bytes are the block's timecode (relative to the cluster timecode) fBlockTimecode = (get1Byte()<<8)|get1Byte(); // The next byte indicates the type of 'lacing' used: u_int8_t c = get1Byte(); c &= 0x6; // we're interested in bits 5-6 only MatroskaLacingType lacingType = (c==0x0)?NoLacing : (c==0x02)?XiphLacing : (c==0x04)?FixedSizeLacing : EBMLLacing; #ifdef DEBUG fprintf(stderr, "\ttrack number %d, timecode %d (=> %f seconds), %s lacing\n", fBlockTrackNumber, fBlockTimecode, (fClusterTimecode+fBlockTimecode)*(fOurFile.fTimecodeScale/1000000000.0), (lacingType==NoLacing)?"no" : (lacingType==XiphLacing)?"Xiph" : (lacingType==FixedSizeLacing)?"fixed-size" : "EBML"); #endif if (lacingType == NoLacing) { fNumFramesInBlock = 1; } else { // The next byte tells us how many frames are present in this block fNumFramesInBlock = get1Byte() + 1; } delete[] fFrameSizesWithinBlock; fFrameSizesWithinBlock = new unsigned[fNumFramesInBlock]; if (fFrameSizesWithinBlock == NULL) break; if (lacingType == NoLacing) { unsigned headerBytesSeen = curOffset() - blockStartPos; if (headerBytesSeen > fBlockSize) break; fFrameSizesWithinBlock[0] = fBlockSize - headerBytesSeen; } else if (lacingType == FixedSizeLacing) { unsigned headerBytesSeen = curOffset() - blockStartPos; if (headerBytesSeen > fBlockSize) break; unsigned frameBytesAvailable = fBlockSize - headerBytesSeen; unsigned constantFrameSize = frameBytesAvailable/fNumFramesInBlock; for (unsigned i = 0; i < fNumFramesInBlock; ++i) { fFrameSizesWithinBlock[i] = constantFrameSize; } // If there are any bytes left over, assign them to the last frame: fFrameSizesWithinBlock[fNumFramesInBlock-1] += frameBytesAvailable%fNumFramesInBlock; } else { // EBML or Xiph lacing unsigned curFrameSize = 0; unsigned frameSizesTotal = 0; unsigned i; for (i = 0; i < fNumFramesInBlock-1; ++i) { if (lacingType == EBMLLacing) { EBMLNumber frameSize; if (!parseEBMLNumber(frameSize)) break; unsigned fsv = (unsigned)frameSize.val(); if (i == 0) { curFrameSize = fsv; } else { // The value we read is a signed value, that's added to the previous frame size, to get the current frame size: unsigned toSubtract = (fsv>0xFFFFFF)?0x07FFFFFF : (fsv>0xFFFF)?0x0FFFFF : (fsv>0xFF)?0x1FFF : 0x3F; int fsv_signed = fsv - toSubtract; curFrameSize += fsv_signed; if ((int)curFrameSize < 0) break; } } else { // Xiph lacing curFrameSize = 0; u_int8_t c; do { c = get1Byte(); curFrameSize += c; } while (c == 0xFF); } fFrameSizesWithinBlock[i] = curFrameSize; frameSizesTotal += curFrameSize; } if (i != fNumFramesInBlock-1) break; // an error occurred within the "for" loop // Compute the size of the final frame within the block (from the block's size, and the frame sizes already computed):) unsigned headerBytesSeen = curOffset() - blockStartPos; if (headerBytesSeen + frameSizesTotal > fBlockSize) break; fFrameSizesWithinBlock[i] = fBlockSize - (headerBytesSeen + frameSizesTotal); } // We're done parsing headers within the block, and (as a result) we now know the sizes of all frames within the block. // If we have 'stripped bytes' that are common to (the front of) all frames, then count them now: if (track->headerStrippedBytesSize != 0) { for (unsigned i = 0; i < fNumFramesInBlock; ++i) fFrameSizesWithinBlock[i] += track->headerStrippedBytesSize; } #ifdef DEBUG fprintf(stderr, "\tThis block contains %d frame(s); size(s):", fNumFramesInBlock); unsigned frameSizesTotal = 0; for (unsigned i = 0; i < fNumFramesInBlock; ++i) { fprintf(stderr, " %d", fFrameSizesWithinBlock[i]); frameSizesTotal += fFrameSizesWithinBlock[i]; } if (fNumFramesInBlock > 1) fprintf(stderr, " (total: %u)", frameSizesTotal); fprintf(stderr, " bytes\n"); #endif // Next, start delivering these frames: fCurrentParseState = DELIVERING_FRAME_WITHIN_BLOCK; fCurOffsetWithinFrame = fNextFrameNumberToDeliver = 0; setParseState(); return; } while (0); // An error occurred. Try to recover: #ifdef DEBUG fprintf(stderr, "parseBlock(): Error parsing data; trying to recover...\n"); #endif fCurrentParseState = LOOKING_FOR_BLOCK; }
unsigned H264VideoStreamParser::parse() { try { // The stream must start with a 0x00000001: if (!fHaveSeenFirstStartCode) { // Skip over any input bytes that precede the first 0x00000001: u_int32_t first4Bytes; while ((first4Bytes = test4Bytes()) != 0x00000001) { get1Byte(); setParseState(); // ensures that we progress over bad data } skipBytes(4); // skip this initial code setParseState(); fHaveSeenFirstStartCode = True; // from now on } if (fOutputStartCodeSize > 0) { // Include a start code in the output: save4Bytes(0x00000001); } // Then save everything up until the next 0x00000001 (4 bytes) or 0x000001 (3 bytes), or we hit EOF. // Also make note of the first byte, because it contains the "nal_unit_type": if (haveSeenEOF()) { // We hit EOF the last time that we tried to parse this data, so we know that any remaining unparsed data // forms a complete NAL unit, and that there's no 'start code' at the end: unsigned remainingDataSize = totNumValidBytes() - curOffset(); while (remainingDataSize > 0) { saveByte(get1Byte()); --remainingDataSize; } if (!fHaveSeenFirstByteOfNALUnit) { // There's no remaining NAL unit. (void)get1Byte(); // forces another read, which will cause EOF to get handled for real this time return 0; } #ifdef DEBUG fprintf(stderr, "This NAL unit (%d bytes) ends with EOF\n", curFrameSize()-fOutputStartCodeSize); #endif } else { u_int32_t next4Bytes = test4Bytes(); if (!fHaveSeenFirstByteOfNALUnit) { fFirstByteOfNALUnit = next4Bytes>>24; fHaveSeenFirstByteOfNALUnit = True; } while (next4Bytes != 0x00000001 && (next4Bytes&0xFFFFFF00) != 0x00000100) { // We save at least some of "next4Bytes". if ((unsigned)(next4Bytes&0xFF) > 1) { // Common case: 0x00000001 or 0x000001 definitely doesn't begin anywhere in "next4Bytes", so we save all of it: save4Bytes(next4Bytes); skipBytes(4); } else { // Save the first byte, and continue testing the rest: saveByte(next4Bytes>>24); skipBytes(1); } setParseState(); // ensures forward progress next4Bytes = test4Bytes(); } // Assert: next4Bytes starts with 0x00000001 or 0x000001, and we've saved all previous bytes (forming a complete NAL unit). // Skip over these remaining bytes, up until the start of the next NAL unit: if (next4Bytes == 0x00000001) { skipBytes(4); } else { skipBytes(3); } } u_int8_t nal_ref_idc = (fFirstByteOfNALUnit&0x60)>>5; u_int8_t nal_unit_type = fFirstByteOfNALUnit&0x1F; fHaveSeenFirstByteOfNALUnit = False; // for the next NAL unit that we parse #ifdef DEBUG fprintf(stderr, "Parsed %d-byte NAL-unit (nal_ref_idc: %d, nal_unit_type: %d (\"%s\"))\n", curFrameSize()-fOutputStartCodeSize, nal_ref_idc, nal_unit_type, nal_unit_type_description[nal_unit_type]); #endif switch (nal_unit_type) { case 6: { // Supplemental enhancement information (SEI) analyze_sei_data(); // Later, perhaps adjust "fPresentationTime" if we saw a "pic_timing" SEI payload??? ##### break; } case 7: { // Sequence parameter set // First, save a copy of this NAL unit, in case the downstream object wants to see it: usingSource()->saveCopyOfSPS(fStartOfFrame + fOutputStartCodeSize, fTo - fStartOfFrame - fOutputStartCodeSize); // Parse this NAL unit to check whether frame rate information is present: unsigned num_units_in_tick, time_scale, fixed_frame_rate_flag; analyze_seq_parameter_set_data(num_units_in_tick, time_scale, fixed_frame_rate_flag); if (time_scale > 0 && num_units_in_tick > 0) { usingSource()->fFrameRate = time_scale/(2.0*num_units_in_tick); #ifdef DEBUG fprintf(stderr, "Set frame rate to %f fps\n", usingSource()->fFrameRate); if (fixed_frame_rate_flag == 0) { fprintf(stderr, "\tWARNING: \"fixed_frame_rate_flag\" was not set\n"); } #endif } else { #ifdef DEBUG fprintf(stderr, "\tThis \"Sequence Parameter Set\" NAL unit contained no frame rate information, so we use a default frame rate of %f fps\n", usingSource()->fFrameRate); #endif } break; } case 8: { // Picture parameter set // Save a copy of this NAL unit, in case the downstream object wants to see it: usingSource()->saveCopyOfPPS(fStartOfFrame + fOutputStartCodeSize, fTo - fStartOfFrame - fOutputStartCodeSize); } } usingSource()->setPresentationTime(); #ifdef DEBUG unsigned long secs = (unsigned long)usingSource()->fPresentationTime.tv_sec; unsigned uSecs = (unsigned)usingSource()->fPresentationTime.tv_usec; fprintf(stderr, "\tPresentation time: %lu.%06u\n", secs, uSecs); #endif // If this NAL unit is a VCL NAL unit, we also scan the start of the next NAL unit, to determine whether this NAL unit // ends the current 'access unit'. We need this information to figure out when to increment "fPresentationTime". // (RTP streamers also need to know this in order to figure out whether or not to set the "M" bit.) Boolean thisNALUnitEndsAccessUnit = False; // until we learn otherwise if (haveSeenEOF()) { // There is no next NAL unit, so we assume that this one ends the current 'access unit': thisNALUnitEndsAccessUnit = True; } else { Boolean const isVCL = nal_unit_type <= 5 && nal_unit_type > 0; // Would need to include type 20 for SVC and MVC ##### if (isVCL) { u_int32_t first4BytesOfNextNALUnit = test4Bytes(); u_int8_t firstByteOfNextNALUnit = first4BytesOfNextNALUnit>>24; u_int8_t next_nal_ref_idc = (firstByteOfNextNALUnit&0x60)>>5; u_int8_t next_nal_unit_type = firstByteOfNextNALUnit&0x1F; if (next_nal_unit_type >= 6) { // The next NAL unit is not a VCL; therefore, we assume that this NAL unit ends the current 'access unit': #ifdef DEBUG fprintf(stderr, "\t(The next NAL unit is not a VCL)\n"); #endif thisNALUnitEndsAccessUnit = True; } else { // The next NAL unit is also a VCL. We need to examine it a little to figure out if it's a different 'access unit'. // (We use many of the criteria described in section 7.4.1.2.4 of the H.264 specification.) Boolean IdrPicFlag = nal_unit_type == 5; Boolean next_IdrPicFlag = next_nal_unit_type == 5; if (next_IdrPicFlag != IdrPicFlag) { // IdrPicFlag differs in value #ifdef DEBUG fprintf(stderr, "\t(IdrPicFlag differs in value)\n"); #endif thisNALUnitEndsAccessUnit = True; } else if (next_nal_ref_idc != nal_ref_idc && next_nal_ref_idc*nal_ref_idc == 0) { // nal_ref_idc differs in value with one of the nal_ref_idc values being equal to 0 #ifdef DEBUG fprintf(stderr, "\t(nal_ref_idc differs in value with one of the nal_ref_idc values being equal to 0)\n"); #endif thisNALUnitEndsAccessUnit = True; } else if ((nal_unit_type == 1 || nal_unit_type == 2 || nal_unit_type == 5) && (next_nal_unit_type == 1 || next_nal_unit_type == 2 || next_nal_unit_type == 5)) { // Both this and the next NAL units begin with a "slice_header". // Parse this (for each), to get parameters that we can compare: // Current NAL unit's "slice_header": unsigned frame_num, pic_parameter_set_id, idr_pic_id; Boolean field_pic_flag, bottom_field_flag; analyze_slice_header(fStartOfFrame + fOutputStartCodeSize, fTo, nal_unit_type, frame_num, pic_parameter_set_id, idr_pic_id, field_pic_flag, bottom_field_flag); // Next NAL unit's "slice_header": #ifdef DEBUG fprintf(stderr, " Next NAL unit's slice_header:\n"); #endif u_int8_t next_slice_header[NUM_NEXT_SLICE_HEADER_BYTES_TO_ANALYZE]; testBytes(next_slice_header, sizeof next_slice_header); unsigned next_frame_num, next_pic_parameter_set_id, next_idr_pic_id; Boolean next_field_pic_flag, next_bottom_field_flag; analyze_slice_header(next_slice_header, &next_slice_header[sizeof next_slice_header], next_nal_unit_type, next_frame_num, next_pic_parameter_set_id, next_idr_pic_id, next_field_pic_flag, next_bottom_field_flag); if (next_frame_num != frame_num) { // frame_num differs in value #ifdef DEBUG fprintf(stderr, "\t(frame_num differs in value)\n"); #endif thisNALUnitEndsAccessUnit = True; } else if (next_pic_parameter_set_id != pic_parameter_set_id) { // pic_parameter_set_id differs in value #ifdef DEBUG fprintf(stderr, "\t(pic_parameter_set_id differs in value)\n"); #endif thisNALUnitEndsAccessUnit = True; } else if (next_field_pic_flag != field_pic_flag) { // field_pic_flag differs in value #ifdef DEBUG fprintf(stderr, "\t(field_pic_flag differs in value)\n"); #endif thisNALUnitEndsAccessUnit = True; } else if (next_bottom_field_flag != bottom_field_flag) { // bottom_field_flag differs in value #ifdef DEBUG fprintf(stderr, "\t(bottom_field_flag differs in value)\n"); #endif thisNALUnitEndsAccessUnit = True; } else if (next_IdrPicFlag == 1 && next_idr_pic_id != idr_pic_id) { // IdrPicFlag is equal to 1 for both and idr_pic_id differs in value // Note: We already know that IdrPicFlag is the same for both. #ifdef DEBUG fprintf(stderr, "\t(IdrPicFlag is equal to 1 for both and idr_pic_id differs in value)\n"); #endif thisNALUnitEndsAccessUnit = True; } } } } } if (thisNALUnitEndsAccessUnit) { #ifdef DEBUG fprintf(stderr, "*****This NAL unit ends the current access unit*****\n"); #endif usingSource()->fPictureEndMarker = True; ++usingSource()->fPictureCount; // Note that the presentation time for the next NAL unit will be different: struct timeval& nextPT = usingSource()->fNextPresentationTime; // alias nextPT = usingSource()->fPresentationTime; double nextFraction = nextPT.tv_usec/1000000.0 + 1/usingSource()->fFrameRate; unsigned nextSecsIncrement = (long)nextFraction; nextPT.tv_sec += (long)nextSecsIncrement; nextPT.tv_usec = (long)((nextFraction - nextSecsIncrement)*1000000); } setParseState(); return curFrameSize(); } catch (int /*e*/) {